How to implement JsonPatch in .NET Core 3.0 Preview 9 correctly?
Asked Answered
C

3

7

I am trying to implement JsonPatch on .NET Core 3.0 Preview 9 web api.

The model:

public class TestPatch
{
    public string TestPath { get; set; }
}

The web api endpoint:

[HttpPatch()]
public async Task<IActionResult> Update([FromBody] JsonPatchDocument<TestPatch> patch)
{
   ...........
   return Ok();
}

The JSON payload:

[
    {
        "op" : "replace",
        "path" : "/testPath",
        "value" : "new value"
    }
]

Using PATCH via Postman, I got this error:

{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|492c592-4f7de4d16a32b942.",
"errors": {
    "$": [
        "The JSON value could not be converted to Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[Test.Models.TestPatch]. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
    ]
}
}

Here's the complete request/response from Postman

PATCH /api/helptemplates HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.16.3
Accept: */*
Cache-Control: no-cache
Postman-Token: a41813ea-14db-4664-98fb-ee30511707bc
Host: localhost:5002
Accept-Encoding: gzip, deflate
Content-Length: 77
Connection: keep-alive
[
{
"op" : "replace",
"path" : "/testPath",
"value" : "new value"
}
]
HTTP/1.1 400 Bad Request
Date: Thu, 12 Sep 2019 21:13:08 GMT
Content-Type: application/problem+json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|492c593-4f7de4d16a32b942.","errors":{"$":["The JSON value could not be converted to Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[Test.Models.TestPatch]. Path: $ | LineNumber: 0 | BytePositionInLine: 1."]}}

JsonPatch Reference:

<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.0.0-preview9.19424.4" />

What is wrong with my code?

Thanks.

Chemoreceptor answered 12/9, 2019 at 21:27 Comment(1)
Posted an issue at github.com/aspnet/AspNetCore/issues/13938 and it was resolved by using Microsoft.AspNetCore.Mvc.NewtonsoftJson.Chemoreceptor
A
12

Support for JsonPatch is enabled using the Microsoft.AspNetCore.Mvc.NewtonsoftJson package. To enable this feature, apps must:

  • Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.

  • Update the project's Startup.ConfigureServices method to include a call to AddNewtonsoftJson

services
    .AddControllers()
    .AddNewtonsoftJson();

AddNewtonsoftJson is compatible with the MVC service registration methods:

  • AddRazorPages
  • AddControllersWithViews
  • AddControllers

But if you are using asp.net core 3.x, then

AddNewtonsoftJson replaces the System.Text.Json based input and output formatters used for formatting all JSON content. To add support for JsonPatch using Newtonsoft.Json, while leaving the other formatters unchanged, update the project's Startup.ConfigureServices as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
    });
}

private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
    var builder = new ServiceCollection()
        .AddLogging()
        .AddMvc()
        .AddNewtonsoftJson()
        .Services.BuildServiceProvider();

    return builder
        .GetRequiredService<IOptions<MvcOptions>>()
        .Value
        .InputFormatters
        .OfType<NewtonsoftJsonPatchInputFormatter>()
        .First();
}

The preceding code requires a reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson and the following using statements:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Linq;

A brief explanation and documentation for the above can be found in this link

Argeliaargent answered 15/1, 2020 at 16:8 Comment(5)
This solution works, but I have a small issue. I have a OutputFormatters set XmlDataContractSerializerOutputFormatter to support XML responses. After your code change all the requests have to explicitly mention the Accept header with application/json. Otherwise XML is returned by default. How do I set Json as the default output formatted?Delanos
services.AddMvc(options => options.EnableEndpointRouting = false) .AddMvcOptions(o => { o.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); o.InputFormatters.Insert(0, GetJsonPatchInputFormatter()); }).AddNewtonsoftJson();Delanos
That is because of the order of your configuration. You need to add services.AddMvc().AddNewtonsodtJson().AddXmlDataContractSerializerFormatters. Refer my EnterpriseArchitecture repository for reference. You need to swap the order of registering output formatters. In Asp.Net Core 3.1 you can just add AddXmlDataContractSerializerFormatters. Please remove XmlDataContractSerializerOutputFormatter from AddMvcOptionsArgeliaargent
Thank you very much. Sorry for the late upvote :)Delanos
Note: as of now, the documentation is incomplete. If you use System.Text.Json with your API but NewtonSoft.Json for JSON PATCH documents, the client must send the Content-Type application/json-patch+json instead of application/json otherwise it doesn't workHandgun
C
5

This answer is for 3.1, but I think it applies to 3.0 as well.... The default json parser in asp.net core 3.x isn't as complete as NewtonsoftJson, so use it until Microsoft implements a feature.

Add this nuget package to your project: Microsoft.AspNetCore.Mvc.NewtonsoftJson

Then add this using statement in startup.cs:

using Newtonsoft.Json.Serialization;

... Then change your ConfigureService() to include the NewtonsoftJson formatter in startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(setupAction =>
    setupAction.ReturnHttpNotAcceptable = true
   ).AddXmlDataContractSerializerFormatters().AddNewtonsoftJson(setupAction =>
   setupAction.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
   //...
}

You may also have to add Accept set to application/json to your requests to prevent them returning XML.

Hope this helps.

Choi answered 8/1, 2020 at 4:31 Comment(0)
U
-1

For those using .NET Core 6.0:

Add the Nuget package: Microsoft.AspNetCore.Mvc.NewtonsoftJson to your project

Then add this using statement to your Program.cs:

using Newtonsoft.Json.Serialization;

Finally add this code to your Program.cs:

builder.Services.AddControllers(options =>
{
    options.ReturnHttpNotAcceptable = true;
}).AddXmlDataContractSerializerFormatters().AddNewtonsoftJson();
Uticas answered 30/12, 2022 at 10:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.