我正在使用 ASP.NET Core 6.0 - Minimal API 開發一個非常簡單的 REST API,對于其中一種Post方法,我需要驗證請求的 json 正文。我用于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; }
}
當 json 請求中的欄位之一不是正確型別時,我的代碼就會出現問題;例如這個示例 json 正文(to不再是string):
{
"to": 12,
"type": "chat",
"message": "Hi!"
}
會引發以下錯誤:
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
顯然它不再可以request轉換為型別的物件,PostRequest但是處理這些情況的正確方法是什么?(定義request為型別object并檢查每個屬性的存在和型別似乎很難看)
Further description: I want to now how I can catch the above mentioned error.
uj5u.com熱心網友回復:
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);
}
}));
uj5u.com熱心網友回復:
以這種方式呼叫最小 API 是有原因的 - 與 MVC 相比,出于簡單性和性能的考慮,許多與系結、模型狀態等相關的便捷功能不存在。
也許有更方便的方法來完成您的任務,但例如您可以利用自定義系結機制來結合 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
};
}
}
}
而在MapPost:
app.MapPost("/API_v1/Send", (ParseJsonAndValidationResult<PostRequest> request) =>
{
// analyze the result ...
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/401348.html
標籤:c# asp.net-core .net-6.0 minimal-apis
