【问题标题】:Throwing custom exceptions on JSON type mismatches ASP.NET Core在 JSON 类型不匹配时引发自定义异常 ASP.NET Core
【发布时间】:2020-01-07 23:08:55
【问题描述】:

当我要转换的 JsonTokenTypeobject/struct 之间存在类型不匹配时,我试图做的是抛出我自己的异常。

比如我的对象是LoginRequest:

public class LoginRequest
{
    [Required]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
}

还有我的控制器:

[HttpPost]
public async Task<IActionResult> CreateCredentialsAsync([FromBody] LoginRequest request)
{
    // Do Stuff
}

但是,如果用户为用户名/密码提供整数而不是字符串(或任何类型不匹配),我想提供自定义异常。

例如,假设客户端使用以下 JSON 正文调用我的服务器:

POST {ip}/api/login
content-type: application/json
{
    "username": 123,
    "password": "password"
}

现在从IAsyncActionFilter 我可以阅读ModelState 并看到它是无效的,但我看不到区分错误原因和抛出不同异常的方法。

我想做的是抛出一个CustomBadRequestException(errorCode: 3, message: "Really, you think that should be a number and not a string"),但如果他们根本没有提供用户名,我想抛出DifferentCustomBadRequestException(errorCode: 2, message: "Nice try hacker")

我是否需要自定义模型绑定器才能执行此操作(甚至扩展现有模型绑定器),或者我是否需要某种反序列化设置和/或转换器,可以根据问题提供更具体的异常,还是两者都有?

额外问题:是否可以在调用操作过滤器之前收集模型状态中的所有错误(这听起来绝对需要自定义模型绑定器,但我想我会问)?

【问题讨论】:

    标签: c# asp.net-core


    【解决方案1】:

    我不确定我是否完全理解您的问题陈述,但假设您想要对模型反序列化进行更多控制,可以对 MVC json 序列化器选项进行一些调整:

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc(o => { })
                    .AddJsonOptions(s =>
                    {
                        s.SerializerSettings.Converters.Add(new Converter()); // one way to gain more control will be to use custom converter for your type, see implementation down below
                        //if you are after something a bit more simple, setting behaviours and handling general error events might work too
                        s.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error; // you probably want that so your missing 
                        s.SerializerSettings.Error = delegate(object sender, ErrorEventArgs args)
                        {
                            // throw your custom exceptions here
                            var message = args.ErrorContext.Error.Message;
                            args.ErrorContext.Handled = false;
                        };
                    });
            }
    

    实现转换器相当简单:

        class Converter : JsonConverter<LoginRequest>
        {
            public override bool CanWrite => false;
    
            public override void WriteJson(JsonWriter writer, LoginRequest value, JsonSerializer serializer)
            {
                throw new NotImplementedException();
            }
    
            public override LoginRequest ReadJson(JsonReader reader, Type objectType, LoginRequest existingValue, bool hasExistingValue, JsonSerializer serializer)
            {
                // your logic here
            }
        }
    

    UPD 在更好地了解您处理原始类型的特定要求之后,尝试摆弄 MVC 序列化程序似乎有点太麻烦了。原因是,您所追求的控制级别(尤其是检查原始类型)似乎在 JsonTextReader 级别上可用,但它似乎压倒一切,这意味着重新实现大量库代码:

    services.AddMvc(o =>
                {
                    o.InputFormatters.RemoveType<JsonInputFormatter>();
                    o.InputFormatters.Add(new MyJsonInputFormatter(logger, serializerSettings, charPool, objectPoolProvider));// there are quite a few parameters that you need to source from somewhere before instantiating your formatter. 
                })
    ....
    class MyJsonInputFormatter: JsonInputFormatter {
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(
          InputFormatterContext context,
          Encoding encoding)
        {
            ...your whole implementation here...
        }
    }
    

    因此,我认为最可行的方法是在 MVC 之前注入自定义中间件,并按照schema validation 的方式为您的原始 json 做一些事情。由于 MVC 需要再次重新读取您的 json(用于模型绑定等),因此您需要查看我的另一个 answer 以满足请求蒸汽倒带的需求。

    【讨论】:

    • 我同意这种特定类型的转换器很容易,但问题是这需要对我系统中的所有类型进行通用检查。任何时候用户发布一个带有 int 的 JSON 主体,而它应该是一个字符串应该抛出相同的错误,或者任何时候用户发布一个带有 bool 而不是 int 的 JSON 主体,等等。
    • 好的,我想我现在更好地理解了您的问题陈述。仔细检查 - 你想检查反序列化类型是否与目标成员类型匹配?
    • 是的,以确保反序列化的类型与目标成员类型匹配。
    猜你喜欢
    • 2018-09-10
    • 2012-09-02
    • 2014-05-23
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    相关资源
    最近更新 更多