Defining an API with swagger: GET call that uses JSON in parameters

前端 未结 2 1015
星月不相逢
星月不相逢 2020-12-18 01:10

I am trying to create a proper, REST API, and document it with Swagger (2.0).

So, I have an API call that is a query, ie, it makes no changes and doesn\'t create any

相关标签:
2条回答
  • 2020-12-18 01:52

    OpenAPI 2.0 (Swagger 2.0)

    OpenAPI 2.0 does not support objects in query strings, it only supports primitive values and arrays of primitives. The most you can do is define your parameter as type: string, add an example of a JSON value, and use description to document the JSON object structure.

    swagger: '2.0'
    ...
    paths:
      /something:
        get:
          parameters:
            - in: query
              name: params
              required: true
              description: A JSON object with the `id` and `name` properties
              type: string
              example: '{"id":4,"name":"foo"}'
    

    OpenAPI 3.0

    JSON in query string can be described using OpenAPI 3.0. In OAS 3, query parameters can be primitives, arrays as well as objects, and you can specify how these parameters should be serialized – flattened into key=value pairs, encoded as a JSON string, and so on.

    For query parameters that contain a JSON string, use the content keyword to define a schema for the JSON data:

    openapi: 3.0.1
    ...
    
    paths:
      /something:
        get:
          parameters:
            - in: query
              name: params
              required: true
    
              # Parameter is an object that should be serialized as JSON
              content:
                application/json:
                  schema:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
    

    This corresponds to the following GET request (before URL encoding):

    GET /something?params={"id":4,"name":"foo"}
    

    or after URL encoding:

    GET /something?params=%7B%22id%3A4%2C%22name%22%3A%22foo%22%7D
    


    Note for Swagger UI users:
    Parameters with content are supported in Swagger UI 3.23.8+ and Swagger Editor 3.6.34+.

    Workaround for earlier versions of UI/Editor:
    Define the parameter as just type: string and add an example of the JSON data. You lose the ability to describe the JSON schema for the query string, but "try it out" will work.

          parameters:
            - in: query
              name: params
              required: true
              schema:
                type: string                    # <-------
              example: '{"id":4,"name":"foo"}'  # <-------
    
    0 讨论(0)
  • 2020-12-18 01:53

    For .Net and Swashbuckle (tested on 3.0) I have a generic class JsonModelBinder that implements IModelBinder interface. The class is used like this:

    public IActionResult SomeAction(
            [FromRoute] int id, 
            [FromQuery][ModelBinder(BinderType = typeof(JsonModelBinder<SomeModel>))] SomeModelquery query) => {}
    

    I have created Operation filter that does the following:

    • Removes parameters created by Swashbuckle from properties of my model
    • Add query parameter of type string

    As a result in Swagger I have a text field where I can insert json and test requests

    public class JsonModelBinderOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, OperationFilterContext context)
        {
            if (operation.Parameters == null || context.ApiDescription.HttpMethod != HttpMethod.Get.ToString())
                return;
            //Find json parameters
            var jsonGetParameters = context.ApiDescription.ActionDescriptor.Parameters.Cast<ControllerParameterDescriptor>()
                .Where(p => p.ParameterInfo.CustomAttributes.Any(c => c.AttributeType == typeof(ModelBinderAttribute) && c.NamedArguments.Any(IsJsonModelBinderType))).ToArray();
    
            if (jsonGetParameters.Length > 0)
            {
                //Select parameters names created by Swagger from json parameters
                var removeParamNames = new HashSet<string>(context.ApiDescription.ParameterDescriptions.Where(d => jsonGetParameters.Any(p => p.Name == d.ParameterDescriptor.Name)).Select(p => p.Name));
                //Create new Swagger parameters from json parameters
                var newParams = jsonGetParameters.Select(p => new NonBodyParameter()
                {
                    In = "query",
                    Name = p.Name,
                    Type = "string",
                    Description = "Json representation of " + p.ParameterType.Name
                });
                //Remove wrong parameters and add new parameters
                operation.Parameters = operation.Parameters.Where(p => p.In != "query" || !removeParamNames.Contains(p.Name)).Concat(newParams).ToList();
            }
        }
    
        private static bool IsJsonModelBinderType(CustomAttributeNamedArgument arg)
        {
            var t = arg.TypedValue.Value as Type;
            return t != null && t.GetGenericTypeDefinition().IsAssignableFrom(typeof(JsonModelBinder<>));
        }
    }
    

    Notes:

    • I use IsAssignableFrom because I have classes derived from JsonModelBinder. You can omit it if you don't inherit
    • You can also omit GetGenericTypeDefinition if your binder is not generic
    • This solution doesn't check for parameter name collision, though you should never have it if the API made with common sense
    0 讨论(0)
提交回复
热议问题