简体   繁体   English

在 JSON 类型不匹配 ASP.NET Core 上抛出自定义异常

[英]Throwing custom exceptions on JSON type mismatches ASP.NET Core

What I am attempting to do is throw my own exceptions when there is a type mismatch between the JsonTokenType and the object/struct I am converting to.我试图做的是在JsonTokenType和我要转换到的object/struct之间存在类型不匹配时抛出我自己的异常。

For example, my object is LoginRequest:例如,我的对象是 LoginRequest:

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

And my controller:还有我的控制器:

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

But if a user provides an integer instead of a string (or really any type mismatch) for the username/password I want to serve a custom exception.但是,如果用户为用户名/密码提供整数而不是字符串(或实际上任何类型不匹配),我想提供自定义异常。

For example imagine the client calls my server with the following JSON body:例如,假设客户端使用以下 JSON 正文调用我的服务器:

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

now from an IAsyncActionFilter I can read the ModelState and see it is invalid, but I don't see a way to differentiate error causes and throw different exceptions.现在从IAsyncActionFilter我可以读取ModelState并看到它是无效的,但我没有看到区分错误原因和抛出不同异常的方法。

What I would like to do is throw a CustomBadRequestException(errorCode: 3, message: "Really, you think that should be a number and not a string") , but if they fail to provide the username at all I want to throw DifferentCustomBadRequestException(errorCode: 2, message: "Nice try hacker")我想做的是抛出一个CustomBadRequestException(errorCode: 3, message: "Really, you think that should be a number and not a string") ,但如果他们根本没有提供用户名,我想抛出DifferentCustomBadRequestException(errorCode: 2, message: "Nice try hacker")

Do I need a custom model binder in order to do this (or even extend an existing model binder), or do I need some sort of deserialization setting and/or converter that can provide more specific exceptions based on what went wrong, or both?我是否需要自定义模型绑定器才能执行此操作(或者甚至扩展现有模型绑定器),或者我是否需要某种反序列化设置和/或转换器,可以根据出现的问题提供更具体的异常,或两者兼而有之?

Bonus question: Is it possible to collect all the errors in the model state before the action filter is called (this sound like it would absolutely require a custom model binder but I figured I would ask)?额外问题:是否可以在调用操作过滤器之前收集模型状态中的所有错误(这听起来绝对需要自定义模型绑定器,但我想我会问)?

I'm not sure I'm fully across your problem statement, but assuming you want more control over model deserialisation, some tweaks can be made to MVC json serialiser options:我不确定我是否完全理解了您的问题陈述,但假设您想要更多地控制模型反序列化,可以对 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;
                    };
                });
        }

implementing Converter is fairly easy:实现 Converter 相当简单:

    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 after getting a better view of your specific requirement re handling primitive types it seems trying to fiddle with MVC serialiser gets a bit too cumbersome. UPD在更好地了解重新处理原始类型的特定要求之后,似乎试图摆弄 MVC 序列化程序有点太麻烦了。 Reason being, the level of control you're after (especially checking primitive types ) seems to be available on JsonTextReader level, but it seems overriding that would mean reimplementing a significant chunk of library code:原因是,您所追求的控制级别(尤其是检查原始类型)似乎在JsonTextReader级别上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...
    }
}

Therefore I think the most viable approach would be injecting custom middleware before MVC and doing something along the lines of schema validation for your raw json.因此,我认为最可行的方法是在 MVC 之前注入自定义中间件,并对原始 json 进行模式验证 Since MVC will need to re-read your json again (for model binding etc), you would want to check out my other answer that caters for request steam rewinding.由于 MVC需要再次重新读取您的 json(用于模型绑定等),您需要查看我的其他回答,以满足请求 Steam 倒带。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM