This is the 30th day of my participation in the August Text Challenge.More challenges in August

  • 📢 welcome to like: 👍 collect ⭐ message 📝 if there are mistakes please correct, give people rose, hand left lingering fragrance!
  • 📢 This article was originally written by Webmote and originally published by Nuggets.
  • 📢 author’s motto: life is toss about, when you don’t toss about life, life will start to toss about you, let us come on together! 💪 💪 💪

1. Extended scenarios

This is Swagger’s advanced application. Since I added frame-level input and output parameter definitions in my previous project, Swagger could not recognize external definitions and only identified them as controller method definitions.

ApiExplorer is a static reflection, you want it to support dynamic filtering, which is really hard to do.

2. The structure of the swagger

I use asp.net core 3.1 and swagger’s package is Swashbuckle.AspNetCore 5.0.0. Why care about the Swagger package, or even the structure, if it’s just generating an API document? I had the urge to let go, introduce the front end, let them understand… .

As a project close to the product, I think it is necessary to have a deep understanding of Swagger’s structure and complete this transformation is perfect.

Speaking of Swagger structure, it is more like OpenAPI structure, this specification should be derived from Swagger, Microsoft defined a set of implementation.

3. OPEN API specification

The root document object of OpenApi is as follows:

#OpenAPI specification version numberOpenapi: 3.0.2
#API metadata information
info:

#Server Connection Information
servers:

#API grouping label
tags: 

#Paths and actions that are valid for the API provided
paths:

#An element that contains multiple schemas, reusable components
components:

#Declare the security mechanism used by the API
security:

#Additional documents
externalDocs:
Copy the code

To change the input and output parameters, we need to change the Paths object and the component object that it references, which encapsulates reusable objects and then references them through the $ref tag.

4. Partial explanation of the Path object

Swagger obtains all controller and method information through ApiExplorer after startup, assembs it, and finally normalizes OpenApi and outputs it to Json file.So take a look at Microsoft OpenApi routines when something doesn’t work. There is very little information on this subject, and almost nothing to draw on.

5. Our goals

Our goal in brief is to have an Api defined as follows: Resp A (Req Req), which is normally described as interface A after swagger parsing, the input parameter is Req, the result is Resp, and after framework filtering, it needs to be parsed as interface A, the input parameter is: Args, Result Result, can also be interpreted as the need to parse the target interface into Result

A (Args

Req).

How do — one of them

Can you stiffen it? We use swagger’s filter to change the parameters and methods defined by the ActionDescriptor. I gave up trying to generate an ActionDescriptor object dynamically, it was too hard, most of the methods were read-only, so you couldn’t modify them, you had to reconstruct a whole new object.

How to do — two

Modify the generated JSON file? Obviously not having a similar interface doesn’t make sense. After going through Swagger’s source code, we found four filters:

  1. IDocumentFilter Indicates the document filter to modify the document description
  2. IOperationFilter ApI-level filter to customize filtering for different apis
  3. ISchemaFilter model filter to modify or add or delete component definition model
  4. IParameterFilter Filter for filtering specific parameters

After screening, there are two kinds of filters can be applied to this activity, IOperationFilter and ISchemaFilter, the last is the most appropriate API level filter!

6. Implementation idea

Above has determined the direction of work, then how to achieve it? You can change the input and output schemas to the input and output schemas defined by the generic type. The code is as follows:

public class MyOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (context.MethodInfo == null) return;
            // Get does not support get
            if(operation.RequestBody ! =null)
            {               
                foreach(var c in operation.RequestBody.Content)
                {
                    var oldRef = c.Value.Schema.Reference;
                    // If the body is a reference to the object definition
                    if(oldRef ! =null)
                    {                       
                        var pt = context.MethodInfo.GetParameters().FirstOrDefault().ParameterType;                        
                        c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiArgs<>).MakeGenericType(pt), context.SchemaRepository);
                    }
                    else if(c.Value.Schema? .Type? .ToLower() =="array")
                    {
                        var pt = context.MethodInfo.ReturnType;
                        c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository); }}}if(operation.Responses ! =null)
            {
                foreach (var c in operation.Responses.Values)
                {
                    varoldRef = c.Content.FirstOrDefault().Value? .Schema.Reference;if(oldRef ! =null)
                    {
                        var pt = context.MethodInfo.ReturnType;
                        c.Content.First().Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository);

                    }
                    else if(c.Content.FirstOrDefault().Value? .Schema.Type? .ToLower()=="array")
                    {
                        var pt = context.MethodInfo.ReturnType;
                        c.Content.First().Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository);
                    }
                }
            }
        }

    }
Copy the code

7. Key points

c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiArgs<>).MakeGenericType(pt), context.SchemaRepository);
Copy the code

This statement adds a new generic object to the OpenAPI component class and assigns it to the original schema without changing anything else.

Results 8.

9. Summary

Oh, oh, oh, good Monday is over!

Routine summary, rational view!

Knot is what ah, knot is I want you to praise but can not get lonely. 😳 😳 😳

👓 have seen this, but also care about a thumbs-up?

👓 has been liked, but also care about a collection?

👓 are collected, but also care about a comment?