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:
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:
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.
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:
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.
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With