首页 > 解决方案 > Newtonsoft.Json MVC:如何为失败的 json 验证自定义 DTO 错误消息

问题描述

我在.NET Core 3.1上有一个 API 项目配置为使用Newtonsoft.Json

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // [...]

    services.AddControllers().AddNewtonsoftJson();

    // [...]
}

Data.cs

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace MyNS
{
    [JsonObject]
    public class MyData
    {
        [JsonProperty("query", Required=Required.Always)]
        public Query Query { get; set; }

        [JsonProperty("limit")]
        public int Limit { get; set; }
    }

    public class Query
    {
        [JsonProperty("props", Required=Required.Always)]
        public List<Prop> Props { get; set; }
    }

    public class Prop
    {
        // Allowed values should be only ("key1" and "key2"). Other strings or types should fail validation
        [JsonProperty("key", Required = Required.Always)]
        public string Key { get; set; }

        [JsonProperty("value", Required = Required.Always)]
        public string Value { get; set; }
    }
}

MyController.cs

// [...]
[Route("test")]
[HttpPost]
public async Task<JsonResult> Test(MyData data)
{
    return Json(data);
}
// [...]

如果请求 json 正文未通过验证,则会向客户端返回带有 400 状态的预配置错误消息:

REQUEST

POST /test
{
  "query": {
    "props": [
      {
        "key": "k1",
        "value": "v1"
      }
    ]
  },
  "limit": "wrong"
}

RESPONSE

{
    "errors": {
        "limit": [
            "Could not convert string to integer: wrong. Path 'limit', line 10, position 18."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|2778ea7e-4dff69c960e985d5."
}

我想使用我选择的 DTO 自定义错误响应,例如:
RESPONSE

"success": false,
"error": "First validation error"

如何配置Newtonsoft.Json为使用自定义 DTO 来处理验证错误?

标签: c#json.netapijson.net

解决方案


找到了一个可能的解决方案:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
    .AddNewtonsoftJson(options => { })
    .ConfigureApiBehaviorOptions(options => {
        options.InvalidModelStateResponseFactory = context =>
        {
            string errorMessage = "Validation error";

            foreach (var key in context.ModelState.Keys)
            {
                if (context.ModelState[key].Errors.Count > 0)
                {
                    errorMessage = string.Format("Json property '{0}': {1}", key, context.ModelState[key].Errors[0].ErrorMessage);
                    break;
                }
            }

            int statusCode = (int)HttpStatusCode.BadRequest;

            var dto = new
            {
                success = false,
                error = errorMessage
            };

            return new JsonResult(dto) { StatusCode = statusCode };
        };
    });
}

推荐阅读