Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
374 views
in Technique[技术] by (71.8m points)

c# - Swagger EF Core 3.1 Delta<Object> Request Body - Show Object Schema not Odata.Delta

I've recently started using Swagger/Swashbuckle in my EF Core project.

When I've added a Patch API to the documentation the Request Body's Example value / Schema is showing details for the Delta not my object. Since it is a subset of the object's properties that need to be posted, is there a way to show my objects schema?

What swagger is showing
What swagger is showing

What I'd like (screen shot is from the Post API call)
What I'd like (screen shot is from the Post API call)

The swagger and API's signature are below.

/// <summary>
/// Updates the provided Absence Reason.
/// </summary>
/// <remarks>
/// Runs bespoke Patch/Validation logic.
/// Updates the Absence Reason then returns the record.
/// </remarks>
/// <param name="key" required="true">Id of the Absence Reason being updated.</param>
/// <param name="delta" required="true">A delta of the updated Absence Reason record.</param>
/// <returns>Returns the created Absence Reason</returns>
/// <response code="204">Returns the updated Absence Reason.</response>
/// <response code="400">Invalid Absence Reason Record, Missing Row Version etc.</response>
/// <response code="404">Absence Reason not found.</response>
/// <response code="412">Record has been updated since the version provided.</response>
[ApiExplorerSettings]
[HttpPatch("odata/[controller]({key})")]
[Produces("application/json")]
[ProducesResponseType(typeof(AbsenceReason), StatusCodes.Status204NoContent)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
[ProducesResponseType(412)]
[Authorize(Policy = AuthPolicyNames.GOIAdminRole)]
public IActionResult Patch([FromODataUri] long key, Delta<AbsenceReason> delta)

Edit to add additional information in response to AlFranco

Screen shot of Watch on Reference Screen shot of Watch on Reference

The _assemblyName from your code is "Microsoft.AspNetCore.OData, Version=7.4.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

The trimmed down XML file created by VS documentation is:

        <member name="M:ESRGOI.Controllers.AbsenceReasonsController.Patch(System.Int64,Microsoft.AspNet.OData.Delta{ESRGOI.Models.AbsenceReason})">
            <summary>
            Updates the provided Absence Reason.
            </summary>
            <param name="key" required="true">Id of the Absence Reason being updated.</param>
            <param name="delta" required="true">A delta of the updated Absence Reason record.</param>
        </member>

Edit 2 Ok, I've managed to come up with a solution based on AlFranco's response.

It should be noted in the Swagger my models don't include the namespace. I originally tried including the namespace but received the below error: /components/schemas/ESRGOI.Models.AbsenceReason does not exist in document

public class DeltaOperationFilter : IOperationFilter
{
  private const string _deltaParam = "Delta";

  public void Apply(OpenApiOperation operation, OperationFilterContext context)
  {
    if (operation.RequestBody == null) return;

    var deltaTypes =
        operation.RequestBody
            .Content
            .Where(x => x.Value.Schema.Reference.Id.EndsWith(_deltaParam));

    foreach (var (_, value) in deltaTypes)
    {
      var schema = value.Schema;
      string model = schema.Reference.Id.Substring(0, schema.Reference.Id.Length - _deltaParam.Length);
      schema.Reference.Id = model;
    }
  }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, so to do this you need to create an IOperationFilter

This worked for me:

    public class DeltaOperationFilter : IOperationFilter
    {
        private const string DeltaWrapper = "Microsoft.AspNet.OData.Delta`1";
        private readonly string _assemblyName = typeof(Microsoft.AspNet.OData.Delta).Assembly.FullName;

        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {                                                               
            if (operation.RequestBody == null) return;
            
            var deltaTypes =
                operation.RequestBody
                    .Content
                    .Where(x => x.Value.Schema.Reference.Id.StartsWith(DeltaWrapper));

            foreach (var (_, value) in deltaTypes)
            {
                var schema = value.Schema;
                var deltaType = Type.GetType(schema.Reference.Id+ ", " + _assemblyName);
                var deltaArgument = deltaType?.GetGenericArguments().First();
                schema.Reference.Id = deltaArgument?.FullName ?? schema.Reference.Id;
            }
        }
    }

The only thing you need to do afterwards is to register in swaggergen

// Assuming you are in Startup.cs ConfigureServices
services.AddSwaggerGen(c => c.OperationFilter<DeltaOperationFilter>());

By the way, are you able to use the Delta<T> apply properly on your entities? I'm going to use patch with some endpoints but would like to use Delta<DTO> and then transform it and apply it to Delta<Entity> that will actually persists. I'm particularly interested in the approach you'd be taking :)

Thanks!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.7k users

...