Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swagger unexpected API PATCH action documentation of JsonPatchDocument in example request body

I'm making a Core 3.1 web API and using JsonPatch to create a PATCH action. I have an action named Patch which has a JsonPatchDocument parameter. Here is the action's signature:

[HttpPatch("{id}")]
public ActionResult<FileRecordDto> Patch(int id, [FromBody] JsonPatchDocument<FileRecordQueryParams> patchDoc)

As I understand, the parameter needs to receive JSON data in the following structure, which I've successfully tested with the action:

[
  {
    "op": "operationName",
    "path": "/propertyName",
    "value": "newPropertyValue"
  }
]

However, the action's documentation generated by Swagger has a different structure: enter image description here

I'm not familiar with this structure and even "value" property is missing from it, which a JsonPatchDocument object has. Every example of patching with the replace operation I've seen has had the first structure.

Why is Swagger generating an alternate structure for a JsonPatchDocument object in the request body for the PATCH endpoint? How do I fix this?

The NuGet package installed for Swagger: Swashbuckle.AspNetCore v5.6.3

like image 581
Lukas Avatar asked Jan 06 '21 16:01

Lukas


People also ask

How to update the swagger Doc in JSON Patch?

We need to make three updates to the swagger doc Remove the internal types for JsonPatchDocument and the internal operations Update the operations to reference the newly registered schema The parameter JsonPatchDocument<...> patchDoc on the patch operation will cause a whole bunch of JsonPatchDocumentOf… and OperationOf… schemas to be registered.

How do I handle JSON Patch requests in an API?

This article explains how to handle JSON Patch requests in an ASP.NET Core web API. To enable JSON Patch support in your app, complete the following steps: Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package. Update the project's Startup.ConfigureServices method to call AddNewtonsoftJson. For example:

What is @jsonpatch?

JSONPatch is unique in that the content-type of the patch document is not the same as the resource. RFC 6902 specifies the application/json-patch format which is the one we’re using here. For completeness we will remove all content types except for application/json-patch+json from our swagger doc while updating the references.

What is the request body in Swagger?

The POST, PUT and PATCH requests can have the request body (payload), such as JSON or XML data. In Swagger terms, the request body is called a body parameter.


1 Answers

Swashbuckle.AspNetCore doesn't work propertly with this type JsonPatchDocument<UpdateModel>, which doesn’t represent the expected patch request doument.

You need to custome a document filter to modify the generated specification.

public class JsonPatchDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var schemas = swaggerDoc.Components.Schemas.ToList();
        foreach (var item in schemas)
        {
            if (item.Key.StartsWith("Operation") || item.Key.StartsWith("JsonPatchDocument"))
                swaggerDoc.Components.Schemas.Remove(item.Key);
        }

        swaggerDoc.Components.Schemas.Add("Operation", new OpenApiSchema
        {
            Type = "object",
            Properties = new Dictionary<string, OpenApiSchema>
            {
                {"op", new OpenApiSchema{ Type = "string" } },
                {"value", new OpenApiSchema{ Type = "string"} },
                {"path", new OpenApiSchema{ Type = "string" } }
            }
        });

        swaggerDoc.Components.Schemas.Add("JsonPatchDocument", new OpenApiSchema
        {
            Type = "array",
            Items = new OpenApiSchema
            {
                Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "Operation" }
            },
            Description = "Array of operations to perform"
        });

        foreach (var path in swaggerDoc.Paths.SelectMany(p => p.Value.Operations)
        .Where(p => p.Key == Microsoft.OpenApi.Models.OperationType.Patch))
        {
            foreach (var item in path.Value.RequestBody.Content.Where(c => c.Key != "application/json-patch+json"))
                path.Value.RequestBody.Content.Remove(item.Key);
            var response = path.Value.RequestBody.Content.Single(c => c.Key == "application/json-patch+json");
            response.Value.Schema = new OpenApiSchema
            {
                Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "JsonPatchDocument" }
            };
        }
    }
}

Register the filter:

services.AddSwaggerGen(c => c.DocumentFilter<JsonPatchDocumentFilter>());

Result:

enter image description here

like image 173
mj1313 Avatar answered Oct 18 '22 22:10

mj1313