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



我使用Blazor WebAssembly(WASM(客户端通过。NET核心REST API。为此,我将通过HTTP PATCH请求发送一个JsonPatchDocument<T>,其中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
}
}
}

这是我的控制器方法:

[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":{}}

我在Postman中看到的错误细节是:

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

我的任何项目都不直接依赖Newtonsoft。我不知道我引用的微软库是否反过来依赖于Newtonsoft。错误表明他们可能会这样做。

这种行为可以在GitHub上的这个小存储库中观察到:https://github.com/BenjaminCharlton/JsonPatchDocumentWithBlazor

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

谢谢

我设法解决了这个困难,Pavel和Enet的意见都很有用,谢谢。

对于其他有同样问题的人,以下是你需要知道的解决方法:

  1. 截至目前(2020年末(,努力向前迈进。NET核心从Newtonsoft.JsonSystem.Text.Json不完整。封装Microsoft.AspNetCore.JsonPatch仍然依赖于Newtonsoft.Json

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

  3. 为了将Newtonsoft用于JsonPatches而不用于其他任何内容,这里描述了一个不错的小技巧,您应该在Web API/服务器项目的Startup类中使用它。特别注意Startup.ConfigureServices内部调用的GetJsonPatchInputFormatter辅助方法的使用

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

  5. 为了解决这个问题,您还必须在Blazor客户端上使用Newtonsoft.Json。你不必把它用于任何事情;你只需要用它来序列化所有的JsonPatches。Newtonsoft.JsonSystem.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);
    }
    

    }

就是这样。这个变通方法对我有效,我希望对你也有效。

相关内容

  • 没有找到相关文章

最新更新