flask-openapi3 icon indicating copy to clipboard operation
flask-openapi3 copied to clipboard

Support multi content type in request body and responses

Open luolingchun opened this issue 11 months ago • 1 comments

Checklist:

  • [x] Run pytest tests and no failed.
  • [x] Run ruff check flask_openapi3 tests examples and no failed.
  • [x] Run mypy flask_openapi3 and no failed.
  • [x] Run mkdocs serve and no failed.

Many people have multi content-type needs:

  • #100
  • #204
  • #207
  • #208

Here is a solution that can specify different content types for each class.

from flask import Request
from pydantic import BaseModel

from flask_openapi3 import OpenAPI

app = OpenAPI(__name__)


class DogBody(BaseModel):
    a: int = None
    b: str = None

    model_config = {
        "openapi_extra": {
            "content_type": "application/vnd.dog+json"
        }
    }


class CatBody(BaseModel):
    c: int = None
    d: str = None

    model_config = {
        "openapi_extra": {
            "content_type": "application/vnd.cat+json"
        }
    }


class BsonModel(BaseModel):
    e: int = None
    f: str = None

    model_config = {
        "openapi_extra": {
            "content_type": "application/bson"
        }
    }


class ContentTypeModel(BaseModel):
    model_config = {
        "openapi_extra": {
            "content_type": "text/csv"
        }
    }


@app.post("/a", responses={200: DogBody | CatBody | ContentTypeModel | BsonModel})
def index_a(body: DogBody | CatBody | ContentTypeModel | BsonModel):
    """
    This may be confusing, if the content-type is application/json, the type of body will be auto parsed to
    DogBody or CatBody, otherwise it cannot be parsed to ContentTypeModel or BsonModel.
    The body is equivalent to the request variable in Flask, and you can use body.data, body.text, etc ...
    """
    print(body)
    if isinstance(body, Request):
        if body.mimetype == "text/csv":
            # processing csv data
            ...
        elif body.mimetype == "application/bson":
            # processing bson data
            obj = BSON(body.data).decode()
            new_body = body.model_validate(obj=obj)
            print(new_body)
    else:
        # DogBody or CatBody
        ...
    return {"hello": "world"}


if __name__ == '__main__':
    app.run(debug=True)

As a result, some break changes had to be generated:

  1. openapi_extra no longer supports description and required in request body, and instead uses request_body_description and request_body_required.
    @app.post("/body", request_body_description="This is post RequestBody")
    def api_error_json(body: BookBody):
        ...
    
  2. openapi_extra no longer supports description , headers and links in response, and replace with the following form.
    @app.get(
         "/test",
         responses={
             "201": {
                 "model": BaseResponse,
                 "description": "Custom description",
                 "headers": {
                     "location": {
                         "description": "URL of the new resource",
                         "schema": {"type": "string"}
                     }
                 },
                 "links": {
                     "dummy": {
                         "description": "dummy link"
                     }
                 }
             }
         }
     )
     def endpoint_test():
         ...
    

luolingchun avatar Jan 04 '25 02:01 luolingchun

@joaopedroft @raisachatterjee @iongion

I'm sorry to bother you, but I hope you can do some testing and give some advice.

I have already released a beta version 4.1.0rc1.

Update in 2025.2.8 4.2.0rc1.

luolingchun avatar Jan 04 '25 03:01 luolingchun