[英]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.