在Asp.net core中间件中访问ModelState

2023-11-24

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

例如我有ResponseFormatterMiddleware在这个中间件中我需要忽略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": null,
    "statusCode": 400,
    "message": "Name is required",
    "version": "V1"
}

但现在观察到的结果是:

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

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();
    }
}

这里的ModelStateFeature是一个保存 ModelState 的虚拟类:

public class ModelStateFeature
{
    public ModelStateDictionary ModelState { get; set; }

    public ModelStateFeature(ModelStateDictionary state)
    {
        this.ModelState= state;
    }
}

为了使动作过滤器自动发生,我们需要配置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 MyModel {
    [MinLength(6)]
    [MaxLength(12)]
    public string Name { get; set; }
    public int Age { get; set; }
}

和一个简单的控制器:

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);
    }
}

enter image description here

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

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"
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在Asp.net core中间件中访问ModelState 的相关文章

  • 没有强命名的代码签名是否会让您的应用程序容易被滥用?

    尝试了解authenticode代码签名和强命名 我是否正确地认为 如果我对引用一些 dll 非强命名 的 exe 进行代码签名 恶意用户就可以替换我的 DLL 并以看似由我签名但正在运行的方式分发应用程序他们的代码 假设这是真的 那么您似
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 关于 C++ 转换:参数 1 从“[some_class]”到“[some_class]&”没有已知的转换

    我正在研究 C 并且遇到了一个错误 我不知道确切的原因 我已经找到了解决方案 但仍然想知道原因 class Base public void something Base b int main Base b b something Base
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new

随机推荐