Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Asp.net核心中间件中的访问模型状态_C#_Asp.net Core_Asp.net Core Middleware_.net Core 2.1 - Fatal编程技术网

C# Asp.net核心中间件中的访问模型状态

C# Asp.net核心中间件中的访问模型状态,c#,asp.net-core,asp.net-core-middleware,.net-core-2.1,C#,Asp.net Core,Asp.net Core Middleware,.net Core 2.1,我需要访问Asp.net Core 2.1中间件中的ModelState,但这只能从Controller访问 例如,我有responseFormatMiddleware,在这个中间件中,我需要忽略ModelState错误,并在“响应消息”中显示它的错误: public class ResponseFormatterMiddleware { private readonly RequestDelegate _next; private readonly ILogger<Res

我需要访问Asp.net Core 2.1中间件中的
ModelState
,但这只能从
Controller
访问

例如,我有
responseFormatMiddleware
,在这个中间件中,我需要忽略
ModelState
错误,并在“响应消息”中显示它的错误:

public class ResponseFormatterMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ResponseFormatterMiddleware> _logger;
    public ResponseFormatterMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
        _logger = loggerFactory?.CreateLogger<ResponseFormatterMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
    }

    public async Task Invoke(HttpContext context)
    {
        var originBody = context.Response.Body;

        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;
            // Process inner middlewares and return result.
            await _next(context);

            responseBody.Seek(0, SeekOrigin.Begin);
            using (var streamReader = new StreamReader(responseBody))
            {
                // Get action result come from mvc pipeline
                var strActionResult = streamReader.ReadToEnd();
                var objActionResult = JsonConvert.DeserializeObject(strActionResult);
                context.Response.Body = originBody;

                // if (!ModelState.IsValid) => Get error message

                // Create uniuqe shape for all responses.
                var responseModel = new GenericResponseModel(objActionResult, (HttpStatusCode)context.Response.StatusCode, context.Items?["Message"]?.ToString());

                // Set all response code to 200 and keep actual status code inside wrapped object.
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(JsonConvert.SerializeObject(responseModel));
            }
        }
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class ResponseFormatterMiddlewareExtensions
{
    public static IApplicationBuilder UseResponseFormatter(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ResponseFormatterMiddleware>();
    }
}

[Serializable]
[DataContract]
public class GenericResponseModel
{
    public GenericResponseModel(object result, HttpStatusCode statusCode, string message)
    {
        StatusCode = (int)statusCode;
        Result = result;
        Message = message;
    }
    [DataMember(Name = "result")]
    public object Result { get; set; }

    [DataMember(Name = "statusCode")]
    public int StatusCode { get; set; }

    [DataMember(Name = "message")]
    public string Message { get; set; }

    [DataMember(Name = "version")]
    public string Version { get; set; } = "V1.0"
}
但现在观察到的结果是:

{
    "result": {
        "Name": [
            "Name is required"
        ]
    },
    "statusCode": 400,
    "message": null,
    "version": "V1"
}

如果您正在实现类似的内容,则可以通过“ActionFilterAttribute”基类的重写方法的参数来访问它

public class ModelStateValidationFilter : ActionFilterAttribute
{
     public override void OnActionExecuting(ActionExecutingContext context)
     {
         // You can access it via context.ModelState
         ModelState.AddModelError("YourFieldName", "Error details...");
         base.OnActionExecuting(context);
     }
}

ModelState
仅在模型绑定后可用。只需使用操作过滤器自动存储
ModelState
,就可以在中间件中使用它

首先,添加一个操作过滤器,将ModelState设置为功能:

public class ModelStateFeatureFilter : IAsyncActionFilter
{

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var state = context.ModelState;
        context.HttpContext.Features.Set<ModelStateFeature>(new ModelStateFeature(state));
        await next();
    }
}
要使动作过滤器自动进行,我们需要配置MVC

services.AddMvc(opts=> {
    opts.Filters.Add(typeof(ModelStateFeatureFilter));
})
现在我们可以在中间件中使用
ModelState
,如下所示:

public class ResponseFormatterMiddleware
{
    // ...

    public async Task Invoke(HttpContext context)
    {
        var originBody = context.Response.Body;

        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;
            // Process inner middlewares and return result.
            await _next(context);

            var ModelState = context.Features.Get<ModelStateFeature>()?.ModelState;
            if (ModelState==null) {
                return ;      //  if you need pass by , just set another flag in feature .
            }

            responseBody.Seek(0, SeekOrigin.Begin);
            using (var streamReader = new StreamReader(responseBody))
            {
                // Get action result come from mvc pipeline
                var strActionResult = streamReader.ReadToEnd();
                var objActionResult = JsonConvert.DeserializeObject(strActionResult);
                context.Response.Body = originBody;

               // Create uniuqe shape for all responses.
                var responseModel = new GenericResponseModel(objActionResult, (HttpStatusCode)context.Response.StatusCode, context.Items?["Message"]?.ToString());

                // => Get error message
                if (!ModelState.IsValid)
                {
                    var errors= ModelState.Values.Where(v => v.Errors.Count > 0)
                        .SelectMany(v=>v.Errors)
                        .Select(v=>v.ErrorMessage)
                        .ToList();
                    responseModel.Result = null;
                    responseModel.Message = String.Join(" ; ",errors) ;
                } 

                // Set all response code to 200 and keep actual status code inside wrapped object.
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(JsonConvert.SerializeObject(responseModel));
            }
        }
    }
}
和一个简单的控制器:

public class HomeController : Controller
{

    public IActionResult Index(string name)
    {
        return new JsonResult(new {
            Name=name
        });
    }

    [HttpPost]
    public IActionResult Person([Bind("Age,Name")]MyModel model)
    {
        return new JsonResult(model);
    }
}

如果我们发送带有有效负载的请求:

POST https://localhost:44386/Home/Person HTTP/1.1
content-type: application/x-www-form-urlencoded

name=helloo&age=20
答复将是:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTE4XEFwcFxBcHBcQXBwXEhvbWVcUGVyc29u?=
X-Powered-By: ASP.NET

{
  "result": {
    "name": "helloo",
    "age": 20
  },
  "statusCode": 200,
  "message": null,
  "version": "V1.0"
}
如果我们发送的请求模型无效:

POST https://localhost:44386/Home/Person HTTP/1.1
content-type: application/x-www-form-urlencoded

name=hello&age=i20
答复将是:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTE4XEFwcFxBcHBcQXBwXEhvbWVcUGVyc29u?=
X-Powered-By: ASP.NET

{
  "result": null,
  "statusCode": 200,
  "message": "The value 'i20' is not valid for Age. ; The field Name must be a string or array type with a minimum length of '6'.",
  "version": "V1.0"
}

我在.net core 2.2中也遇到了一些问题,似乎
IAsyncationFilter
在我的案例中不起作用,但使用了
IActionResult
。下面是我修改过的代码,但不确定这是否是我想要的

public class ModelStateFeatureFilter : IActionResult
{
    public Task ExecuteResultAsync(ActionContext context)
    {
        var state = context.ModelState;
        context.HttpContext.Features.Set(new ModelStateFeature(state));
        return Task.CompletedTask;
    }
} 
和下面的启动类

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = ctx => new ModelStateFeatureFilter();
});
services.Configure(选项=>
{
options.InvalidModelStateResponseFactory=ctx=>new-ModelStateFeatureFilter();
});

是的,我知道,但我需要它在Middleware什么样的
中间件?您能描述一下访问
ModelState
的目的吗?确切地说,我需要一些模型绑定信号我没有得到它。为什么?你想忽略什么?在什么情况下使用?假设一个属性可以是单属性,也可以是多属性,这取决于AllowMultiple属性,在这种情况下,可能可以使用
ModelState
在一般中间件中根本不存在。这是一个MVC概念。使用这种方法,当ModelState.IsValid==false时,我很难启动OnActionExecutionAsync。很明显,当你回答这个问题时,它起了作用,所以我想知道asp.net core 2.2中是否有什么变化?@raterus我只是在asp.net core 2.2中尝试上面的代码,它对我来说很好。您是否在
UseMvc()
之后注册了中间件。如果是这样,它就不会生效。并确保已将
modelstateffeaturefilter
添加到MVC服务中。
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTE4XEFwcFxBcHBcQXBwXEhvbWVcUGVyc29u?=
X-Powered-By: ASP.NET

{
  "result": null,
  "statusCode": 200,
  "message": "The value 'i20' is not valid for Age. ; The field Name must be a string or array type with a minimum length of '6'.",
  "version": "V1.0"
}
public class ModelStateFeatureFilter : IActionResult
{
    public Task ExecuteResultAsync(ActionContext context)
    {
        var state = context.ModelState;
        context.HttpContext.Features.Set(new ModelStateFeature(state));
        return Task.CompletedTask;
    }
} 
services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = ctx => new ModelStateFeatureFilter();
});