C# Asp.net核心中间件中的访问模型状态
我需要访问Asp.net Core 2.1中间件中的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
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();
});