File: Infrastructure\ModelStateInvalidFilter.cs
Web Access
Project: src\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable enable
 
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
 
namespace Microsoft.AspNetCore.Mvc.Infrastructure;
 
/// <summary>
/// A <see cref="IActionFilter"/> that responds to invalid <see cref="ActionContext.ModelState"/>. This filter is
/// added to all types and actions annotated with <see cref="ApiControllerAttribute"/>.
/// See <see cref="ApiBehaviorOptions"/> for ways to configure this filter.
/// </summary>
public partial class ModelStateInvalidFilter : IActionFilter, IOrderedFilter
{
    internal const int FilterOrder = -2000;
 
    private readonly ApiBehaviorOptions _apiBehaviorOptions;
    private readonly ILogger _logger;
 
    /// <summary>
    /// Initializes a new instance of <see cref="ModelStateInvalidFilter"/>.
    /// </summary>
    /// <param name="apiBehaviorOptions">The api behavior options.</param>
    /// <param name="logger">The logger.</param>
    public ModelStateInvalidFilter(ApiBehaviorOptions apiBehaviorOptions, ILogger logger)
    {
        _apiBehaviorOptions = apiBehaviorOptions ?? throw new ArgumentNullException(nameof(apiBehaviorOptions));
        if (!_apiBehaviorOptions.SuppressModelStateInvalidFilter && _apiBehaviorOptions.InvalidModelStateResponseFactory == null)
        {
            throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                typeof(ApiBehaviorOptions),
                nameof(ApiBehaviorOptions.InvalidModelStateResponseFactory)));
        }
 
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
 
    /// <summary>
    /// Gets the order value for determining the order of execution of filters. Filters execute in
    /// ascending numeric value of the <see cref="Order"/> property.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Filters are executed in a sequence determined by an ascending sort of the <see cref="Order"/> property.
    /// </para>
    /// <para>
    /// The default Order for this attribute is -2000 so that it runs early in the pipeline.
    /// </para>
    /// <para>
    /// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
    /// </para>
    /// </remarks>
    public int Order => FilterOrder;
 
    /// <inheritdoc />
    public bool IsReusable => true;
 
    /// <summary>
    /// Invoked when an action is executed.
    /// </summary>
    /// <param name="context">The <see cref="ActionExecutedContext"/>.</param>
    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
 
    /// <summary>
    /// Invoked when an action is executing.
    /// </summary>
    /// <param name="context">The <see cref="ActionExecutingContext"/>.</param>
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.Result == null && !context.ModelState.IsValid)
        {
            Log.ModelStateInvalidFilterExecuting(_logger);
            context.Result = _apiBehaviorOptions.InvalidModelStateResponseFactory(context);
        }
    }
 
    private static partial class Log
    {
        [LoggerMessage(1, LogLevel.Debug, "The request has model state errors, returning an error response.", EventName = "ModelStateInvalidFilterExecuting")]
        public static partial void ModelStateInvalidFilterExecuting(ILogger logger);
    }
}