.net core 将JsonPatchDocument与Blazor一起使用时出现意外错误

.net core 将JsonPatchDocument与Blazor一起使用时出现意外错误,.net-core,asp.net-core-webapi,blazor,blazor-client-side,blazor-webassembly,.net Core,Asp.net Core Webapi,Blazor,Blazor Client Side,Blazor Webassembly,我使用Blazor WebAssembly(WASM)客户端通过.NET核心REST API执行更新。为此,我通过HTTP补丁请求发送一个JsonPatchDocument,其中T是我的应用程序的数据传输对象(DTO)之一 它不起作用。我在Blazor应用程序中返回了一个500内部服务器错误状态代码。我在《邮递员》中得到了更多的细节,但还不足以让我理解这个问题 以下是我的Blazor WASM应用程序中的调用代码: @code { [Parameter] public int BookId {

我使用Blazor WebAssembly(WASM)客户端通过.NET核心REST API执行更新。为此,我通过HTTP补丁请求发送一个
JsonPatchDocument
,其中
T
是我的应用程序的数据传输对象(DTO)之一

它不起作用。我在Blazor应用程序中返回了一个500内部服务器错误状态代码。我在《邮递员》中得到了更多的细节,但还不足以让我理解这个问题

以下是我的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
    }
}
}
当序列化为JSON时,我发送的修补程序文档如下所示:

{"Operations":[{"value":"How to make JsonPatchDocument work with Blazor - Second Edition","OperationType":2,"path":"/Title","op":"replace","from":null}],"ContractResolver":{}}
 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);
 }
我在《邮递员》中看到的错误细节是:

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, JsonSerializerOptions options, 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, Task lastTask, State next, Scope scope, Object state, 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
System.NotSupportedException:不支持接口类型的反序列化。键入“Newtonsoft.Json.Serialization.IContractResolver”
位于System.Text.Json.ThrowHelper.ThrownNotSupportedException_反序列化CreateObjectDelegateIsNull(类型invalidType)
位于System.Text.Json.JsonSerializer.handleStarObject(JsonSerializerOptions选项,ReadStack和state)
位于System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions选项、Utf8JsonReader和reader、ReadStack和ReadStack)
位于System.Text.Json.JsonSerializer.ReadCore(JsonReaderState和readerState、布尔值isFinalBlock、ReadOnlySpan`1缓冲区、JsonSerializerOptions选项、ReadStack和ReadStack)
在System.Text.Json.JsonSerializer.ReadAsync[TValue](流utf8Json,类型returnType,JsonSerializerOptions选项,CancellationToken CancellationToken)
位于Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext,编码)
位于Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext,编码)
位于Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext绑定上下文)
位于Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext ActionContext、IModelBinder modelBinder、IValueProvider valueProvider、ParameterDescriptor参数、ModelMetadata元数据、对象值)
在Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.c_uuDisplayClass0_0.d.MoveNext()中
---来自引发异常的上一个位置的堆栈结束跟踪---
在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g_u等待| 13_0(ControllerActionInvoker invoker,Task lastTask,State next,Scope Scope,Object State,Boolean isCompleted)
在Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g_|24_0(ResourceInvoker invoker、Task lastTask、State next、Scope Scope、Object State、Boolean isCompleted)
位于Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed上下文)
位于Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(状态和下一步、范围和范围、对象和状态、布尔值和isCompleted)
在Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()中
---来自引发异常的上一个位置的堆栈结束跟踪---
在Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g|u waiting | 17|0(ResourceInvoker invoker,Task Task,IDisposable作用域)
位于Microsoft.AspNetCore.Routing.EndpointMiddleware.g_uwaitRequestTask | 6_0(端点、任务请求任务、ILogger记录器)
位于Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext上下文)
位于Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext上下文)
位于Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext上下文)
标题
=======
接受:*/*
接受编码:gzip,deflate,br
缓存控制:没有缓存
连接:保持活力
内容长度:175
内容类型:application/json
主机:本地主机:44367
用户代理:PostmanRuntime/7.26.3
邮递代币:b4444f41-b80f-4ef5-92d5-2416d68d471e
我的任何项目都不直接依赖Newtonsoft。不过,我不知道我引用的Microsoft库是否依赖于Newtonsoft。这个错误表明他们可能会这样做

在GitHub上的这个小存储库中可以观察到这种行为:

有人知道它为什么不工作和/或什么能解决它吗


谢谢

我设法解决了这个难题,Pavel和Enet的意见非常有用,谢谢

对于其他有相同问题的人,您需要了解以下内容来解决此问题:

  • 到目前为止(2020年末),将.NET核心从
    Newtonsoft.Json
    移动到
    System.Text.Json
    的努力还没有完成。包
    Microsoft.AspNetCore.JsonPatch
    仍然依赖于
    Newtonsoft.Json

  • NET核心开发团队知道GitHub在报告这方面存在很多问题。但它们都没有采取任何行动就被关闭了。显然,将
    Microsoft.AspNetCore.JsonPatch
    切换到
    System.Text.Json
    需要付出太多的努力

  • 将Newtonsoft用于
    JsonPatch
    es,但不用于Web API/server项目的
    Startup
    类中应使用的任何其他内容。请特别注意
    GetJsonPatchInputFormatter
    helper方法的使用,该方法在
    Startup.ConfigureServices

  • 但这本身可能无法解决Blazor WASM/客户端项目将收到的50X和40X HTTP错误,因为如果使用
    System.Text.Json
    序列化
    JsonPatch
    ,它会在Json字符串的末尾添加一个空的
    ContractResolver
    对象(看起来像
    ,“ContractResolver”:{}
    ),它在服务器端中断。由于某些原因,该请求与您所做的任何控制器路由都不匹配

  • 要解决这个问题,还必须在Blazor客户端上使用
    Newtonsoft.Json
    。你不必事事都用它;你只需要使用
    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, JsonSerializerOptions options, 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, Task lastTask, State next, Scope scope, Object state, 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
    
     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);
     }