如何解决在Blazor中使用JsonPatchDocument发生意外错误
我正在使用Blazor WebAssembly(WASM)客户端通过.NET Core REST API执行更新。为此,我通过HTTP PATCH请求发送了JsonPatchDocument<T>
,其中T
是我应用程序的数据传输对象(DTO)之一。
它不起作用。我在Blazor应用程序中返回了500个内部服务器错误状态代码。我在Postman中了解了更多细节,但不足以让我理解问题。
这是我的Blazor WASM应用程序中的调用代码:
@code
{
[Parameter]
public int BookId { get; set; } = 101;
private async Task HandleClickAsync()
{
string newTitle = "How to make JsonPatchDocument work with Blazor - Second Edition";
var patchDocument = new JsonPatchDocument<Book>()
.Replace(c => c.Title,newTitle);
var json = JsonSerializer.Serialize(patchDocument);
var content = new StringContent(json,Encoding.UTF8,"application/json-patch+json");
var response = await HttpClient.PatchAsync($"https://localhost:44367/api/books/{BookId}",content);
if (response.IsSuccessStatusCode)
{
// Handle success
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
// Handle not found
}
else
{
// Handle unexpected failures
}
}
}
这是我的控制器方法:
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
[HttpPatch("{id:int}")]
public async Task<ActionResult> PatchAsync(
int id,[FromBody] JsonPatchDocument<Book> patch)
{
// We're just going to fake an asynchronous database call and return a 200 status code to the client
await Task.FromResult(true);
return Ok();
}
}
这是我的DTO:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
}
我发送的补丁文档序列化为JSON时,如下所示:
{"Operations":[{"value":"How to make JsonPatchDocument work with Blazor - Second Edition","OperationType":2,"path":"/Title","op":"replace","from":null}],"ContractResolver":{}}
我在邮递员中看到的错误详细信息是:
System.NotSupportedException: Deserialization of interface types is not supported. Type 'Newtonsoft.Json.Serialization.IContractResolver'
at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(Type invalidType)
at System.Text.Json.JsonSerializer.HandleStartObject(JsonSerializerOptions options,ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options,Utf8JsonReader& reader,ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadCore(JsonReaderState& readerState,Boolean isFinalBlock,ReadOnlySpan`1 buffer,JsonSerializerOptions options,ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadAsync[TValue](Stream utf8Json,Type returnType,CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context,Encoding encoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context,Encoding encoding)
at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext,IModelBinder modelBinder,IValueProvider valueProvider,ParameterDescriptor parameter,ModelMetadata metadata,Object value)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker,Task lastTask,State next,Scope scope,Object state,Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker,Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next,Scope& scope,Object& state,Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker,Task task,IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint,Task requestTask,ILogger logger)
at Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Accept-Encoding: gzip,deflate,br
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 175
Content-Type: application/json
Host: localhost:44367
User-Agent: PostmanRuntime/7.26.3
Postman-Token: b4444f41-b80f-4ef5-92d5-2416d68d471e
我的项目中没有一个直接依赖Newtonsoft。我不知道我引用的Microsoft库是否依赖于Newtonsoft。该错误表明也许他们这样做。
该行为可以在GitHub上的这个小存储库中观察到: https://github.com/BenjaminCharlton/JsonPatchDocumentWithBlazor
请问有人知道为什么它不起作用和/或会解决什么问题吗?
谢谢
解决方法
我设法解决了这一难题,Pavel和Enet的输入都很有用,谢谢。
对于其他遇到相同问题的人,这是您需要解决的问题:
-
截至目前(2020年末),将.NET Core从
Newtonsoft.Json
移至System.Text.Json
的努力还没有完成。软件包Microsoft.AspNetCore.JsonPatch
仍然取决于Newtonsoft.Json
。 -
.NET Core开发团队意识到GitHub在报告此问题方面存在很多问题。但是他们都没有采取任何行动而被关闭。显然,将
Microsoft.AspNetCore.JsonPatch
切换到System.Text.Json
涉及很多工作。 -
要将Newtonsoft用于
内部调用的JsonPatch
,而不能将其他there is a nice little hack described here用于Web API /服务器项目的Startup
类中。特别注意在GetJsonPatchInputFormatter
Startup.ConfigureServices
辅助方法的使用 -
但是,单独使用它可能无法解决Blazor WASM /客户端项目将收到的50X和40X HTTP错误,因为如果使用
JsonPatch
序列化System.Text.Json
,它将添加一个空的ContractResolver
对象到JSON字符串的末尾(看起来像,"ContractResolver":{}
),它在服务器端中断。由于某种原因,该请求与您所执行的任何控制器路由都不匹配。 -
要解决此问题,您还必须在Blazor客户端上使用
Newtonsoft.Json
。您不必将其用于所有内容;您只需要使用它来序列化所有JsonPatch
。与Newtonsoft.Json
相比,System.Text.Json
的代码行多了几行,但是我做了一个扩展方法,因此不会在各处重复。扩展方法如下:public static class HttpClientExtensions { public static async Task<HttpResponseMessage> PatchAsync<T>(this HttpClient client,string requestUri,JsonPatchDocument<T> patchDocument) where T : class { var writer = new StringWriter(); var serializer = new JsonSerializer(); serializer.Serialize(writer,patchDocument); var json = writer.ToString(); var content = new StringContent(json,Encoding.UTF8,"application/json-patch+json"); return await client.PatchAsync(requestUri,content); }
}
就是这样。此解决方法对我有用,我希望对您也有用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。