简体   繁体   English

ASP.NET Core 6.0 - 最小 API:将 json 主体请求绑定到所需 class 的正确方法是什么?

[英]ASP.NET Core 6.0 - Minimal APIs: What is the proper way to bind json body request to desired class?

I'm developing a very simple REST API using ASP.NET Core 6.0 - Minimal APIs and for one of Post methods, I need to validate the json body of the request. I'm developing a very simple REST API using ASP.NET Core 6.0 - Minimal APIs and for one of Post methods, I need to validate the json body of the request. I used System.ComponentModel.DataAnnotations for that purpose and the code is working fine:我为此目的使用了System.ComponentModel.DataAnnotations并且代码运行良好:

using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/API_v1/Send", (PostRequest request) =>
{
    ICollection<ValidationResult> ValidationResults = null;
    if (Validate(request, out ValidationResults))
    {
        //request object is valid and has proper values
        //the rest of the logic...
    }
    return new { status = "failed"};
});

app.Run();

static bool Validate<T>(T obj, out ICollection<ValidationResult> results)
{
    results = new List<ValidationResult>();
    return Validator.TryValidateObject(obj, new ValidationContext(obj), results, true);
}


public class PostRequest
{
    [Required]
    [MinLength(1)]
    public string To { get; set; }
    [Required]
    [RegularExpression("chat|groupchat")]
    public string Type { get; set; }
    [Required]
    public string Message { get; set; }
}

Problem with my code arises when one of the fields in the json request is not of the proper type;当 json 请求中的字段之一不是正确的类型时,我的代码出现问题; For example this sample json body ( to is no longer a string ):例如此示例 json 正文( to不再是string ):

{
    "to": 12,
    "type": "chat",
    "message": "Hi!"
}

would raise the following error:会引发以下错误:

Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter "PostRequest request" from the request body as JSON.
 ---> System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $.to | LineNumber: 1 | BytePositionInLine: 12.
 ---> System.InvalidOperationException: Cannot get the value of a token type 'Number' as a string.
   at System.Text.Json.Utf8JsonReader.GetString()
   at System.Text.Json.Serialization.Converters.StringConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   --- End of inner exception stack trace ---
   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan`1 buffer, JsonSerializerOptions options, ReadStack& state, JsonConverter converterBase)
   at System.Text.Json.JsonSerializer.ContinueDeserialize[TValue](ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, JsonConverter converter, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.ReadAllAsync[TValue](Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_3.<<HandleRequestBodyAndCompileRequestDelegate>b__2>d.MoveNext()
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.InvalidJsonRequestBody(HttpContext httpContext, String parameterTypeName, String parameterName, Exception exception, Boolean shouldThrow)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_3.<<HandleRequestBodyAndCompileRequestDelegate>b__2>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5090
User-Agent: PostmanRuntime/7.26.8
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 62
Postman-Token: e31d3575-d2ec-49a7-9bef-04eaecf38a24

Obviously it no longer can cast request into an object of type PostRequest but what is the proper way to handle these kind of situations?显然它不再可以将request转换为 PostRequest 类型的PostRequest但是处理这种情况的正确方法是什么? (defining request as type object and checking for the presence and type of every property seems ugly) (将request定义为object类型并检查每个属性的存在和类型似乎很难看)

Further description : I want to now how I can catch the above mentioned error.进一步描述:我现在想知道如何捕获上述错误。

Asp.Net Core provides a way to register exception handlers: Asp.Net Core 提供了一种注册异常处理程序的方法:

app.UseExceptionHandler(c => c.Run(async context =>
{
    var exception = context.Features
        .Get<IExceptionHandlerFeature>()
        ?.Error;
    if (exception is not null)
    {
        var response = new { error = exception.Message };
        context.Response.StatusCode = 400;

        await context.Response.WriteAsJsonAsync(response);
    }
}));

Minimal API is called that way for a reason - many convenient features related to binding, model state and so on are not present compared to MVC in sake of simplicity and performance.最小的 API 被称为这种方式是有原因的 - 与 MVC 相比,与绑定相关的许多方便的功能,model state 等与 MVC 相比,为了简单和性能,不存在。

Maybe there is more convenient approach to your task but for example you can leverage custom binding mechanism to combine the json parsing and validation:也许有更方便的方法来完成您的任务,但例如您可以利用自定义绑定机制来结合 json 解析和验证:

public class ParseJsonAndValidationResult<T>
{
    public T? Result { get; init; }
    public bool Success { get; init; }
    // TODO - add errors

    public static async ValueTask<ParseJsonAndValidationResult<T>?> BindAsync(HttpContext context)
    {
        try
        {
            var result = await context.Request.ReadFromJsonAsync<T>(context.RequestAborted);
            var validationResults = new List<ValidationResult>();
            if (!Validator.TryValidateObject(result, new ValidationContext(result), validationResults, true))
            {
                // TODO - add errors
                return new ParseJsonAndValidationResult<T>
                {
                    Success = false
                };
            }

            return new ParseJsonAndValidationResult<T>
            {
                Result = result,
                Success = true
            };
        }
        catch (Exception ex)
        {
            // TODO - add errors
            return new ParseJsonAndValidationResult<T>
            {
                Success = false
            };
        }

    }
}

And in the MapPost :MapPost中:

app.MapPost("/API_v1/Send", (ParseJsonAndValidationResult<PostRequest> request) =>
{ 
    // analyze the result ...
});

暂无
暂无

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

相关问题 ASP.NET Core 6.0 - 最少的 API:Map 方法可以使用哪些参数来处理路由? - ASP.NET Core 6.0 - Minimal APIs: What parameters are available to Map methods for handling routes? 是否有一种开箱即用的方式将 HTTP 请求的整个主体绑定到 ASP.NET Core 控制器操作中的字符串参数? - Is there an out of the box way to bind the entire body of an HTTP request to a string parameter in an ASP.NET Core controller action? ASP.NET核心在json post请求体中传递antiforgerytoken - ASP.NET Core pass antiforgerytoken in json post request body 如何在 ASP.NET Core 5 Web API 中绑定来自请求主体的简单类型 - How can I bind simple type coming from body of the request in ASP.NET Core 5 Web API 如何将 ASP.NET 核心最小 API 演进为完整的 controller model - How to evolve an ASP.NET Core Minimal APIs to a full controller model 如何在asp.net核心中将json请求正文验证为有效的json - How to validate json request body as valid json in asp.net core 无法将 POST 请求正文中的 json 绑定到 .net-core 中的 class 参数 - Can't bind json in POST request body to class argument in .net-core asp.net Web api:将请求正文值绑定到模型类 - asp.net web api : bind request body values to the model class 在 ASP.NET 上检测 JSON 请求的最佳方法是什么 - What's the best way to detect a JSON request on ASP.NET ASP.NET Core - 绑定多个controller参数到body - ASP.NET Core - bind multiple controller parameters to body
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM