[PYTHON] How to add arbitrary headers to response with FastAPI

In FastAPI, it was necessary to add a response header as a countermeasure against vulnerabilities.

I wanted to add the same header to every response, but the ~~ FastAPI docs didn't mention it ~~ (found), and the initial path operation function (eg @ app.post ("/ comment") I didn't know how to set it as a whole with only the settings for each) decorator). The main thing I would like to introduce this time is how to add it to all responses, but since it is a big deal here, I will introduce both methods in a cross-cutting manner.

In the following example, let's set Cache-Control assuming an API that returns images and so on. Of course, whether it's Content-Security-Policy or X-Frame-Options, you can set it as well.

1. How to add to a path operation function

This is the method in the Fast API Official Documentation.

Add a class of type Response to the argument of the path operation function as shown below.

from fastapi import Response

@app.get("/image")
def image(response: Response)
    response.headers["Cache-Control"] = "no-cache, no-store"
    ...
    return image_instance

As a result, the response that was originally like this

response_before.png

You can see that "Cache-Control" has been added.

response_after.png

2. How to add to all resources

Method in this document is available.

@app.middleware("http")
async def add_my_headers(request: Request, call_next):
    response = await call_next(request)
    response.headers["Cache-Control"] = "no-cache, no-store"
    return response

It is possible to set multiple headers by changing the key of response.headers.

It can also be written using a class called BaseHTTPMiddleware in a library called Starlette on which FastAPI depends.

from starlette.middleware.base import BaseHTTPMiddleware

class MyHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers["Cache-Control"] = "no-cache, no-store"
        return response

In this case, set the class created above for the FastAPI instance defined in main.py etc.

app = FastAPI()

app.add_middleware(MyHeadersMiddleware)

By the way, for CORS, a dedicated CORSMiddleware is prepared, and it supports whitelisting by regex, so it is better to use this without implementing as above. Let's do it. You can read more about this in the Fast API Tutorial (https://fastapi.tiangolo.com/tutorial/cors/). (The fastapi.middleware.cors.CORSMiddleware class is actually just an alias for starlette.middleware.cors.CORSMiddleware.)

Be careful of error handling

If an Exception is raised or error handling is performed by Pydantic validation, the header will be added without any problem even in the error case with method 2, but the set header will not be output with method 1.

Of course, even with method 1, if you handle the error handling directly in the path operation function as shown in the official document of FastAPI, it will be output without any problem.

    if error:
        return JSONResponse(
            {}, status_code=404, headers={"Cache-Control": "no-cache, no-store"}
        )

However, if you handle this for all error cases, there is a risk of processing omissions, so in most cases you will probably have to define @ app.exception_handler to handle it. I think that it will be complicated if you write the processing for each path operation function in this, so in many cases, adopt method 2 rather than method 1 and rewrite the header according to the content of the response and the content of the error. I think it would be better to give it to you.

Summary

So far, we have introduced two ways to add arbitrary HTTP headers to the Fast API response.

I use FastAPI for business, so I would like to write some articles in a series in the future.

Recommended Posts

How to add arbitrary headers to response with FastAPI
How to add a package with PyCharm
How to use FastAPI ③ OpenAPI
How to update with SQLAlchemy?
How to cast with Theano
How to Alter with SQLAlchemy?
How to separate strings with','
How to RDP with Fedora31
How to Delete with SQLAlchemy?
How to cancel RT with tweepy
Add fields to features with ArcPy
How to use virtualenv with PowerShell
How to deal with imbalanced data
How to install python-pip with ubuntu20.04LTS
How to deal with imbalanced data
Add response headers using Bottle hook
How to add sudo when debugging
How to get started with Scrapy
How to get started with Python
How to deal with DistributionNotFound errors
How to get started with Django
How to Data Augmentation with PyTorch
How to use FTP with Python
How to calculate date with python
How to add AWS EBS volume
How to install mysql-connector with pip3
How to INNER JOIN with SQLAlchemy
How to install Anaconda with pyenv
How to authenticate with Django Part 2
How to authenticate with Django Part 3
[Hugo] Summary of how to add pages to sites built with Learn
How to create a heatmap with an arbitrary domain in Python
How to do arithmetic with Django template
How to use FastAPI ① Tutorial --User Guide
[Blender] How to set shape_key with script
How to title multiple figures with matplotlib
How to get parent id with sqlalchemy
How to use FastAPI ② Advanced --User Guide
How to install DLIB with 2020 / CUDA enabled
How to use ManyToManyField with Django's Admin
How to use OpenVPN with Ubuntu 18.04.3 LTS
How to use Cmder with PyCharm (Windows)
How to prevent package updates with apt
How to work with BigQuery in Python
How to use Ass / Alembic with HtoA
How to deal with enum compatibility errors
How to use Japanese with NLTK plot
How to do portmanteau test with python
How to search Google Drive with Google Colaboratory
How to display python Japanese with lolipop
How to download youtube videos with youtube-dl
Add images to iOS photos with Pythonista
How to use jupyter notebook with ABCI
How to power off Linux with Ultra96-V2
"How to pass PATH" to learn with homebrew
How to scrape websites created with SPA
How to use CUT command (with sample)
Add Gaussian noise to images with python2.7
How to enter Japanese with Python curses
How to add python module to anaconda environment
[Python] How to deal with module errors