Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returning a RedirectResponse in a dependency #1039

Closed
philmade opened this issue Feb 26, 2020 · 13 comments
Closed

Returning a RedirectResponse in a dependency #1039

philmade opened this issue Feb 26, 2020 · 13 comments
Labels
question Question or problem question-migrate

Comments

@philmade
Copy link

philmade commented Feb 26, 2020

Description

How can I return a Redirect in a dependency to be executed in a conditional? In the example below, the code works, but I just get an exception. I want a redirect.:

def logged_in(user: models.User = Depends(current_user)):
    if not user.is_authenticated:
        raise HTTPException(status_code=303, detail="You need to login")

@users.get('/channel/{username}', dependencies=[Depends(logged_in)])
def channel(*, username: str,
            request: Request,
            current_user: models.User = Depends(current_user),
            db: Session = Depends(get_db))
    do_something()

Below is what I want, but when I use this dependency nothing happens because doesn't use the return value. I can't think of a way to do this without referencing the Dependency directly.

def logged_in(user: models.User = Depends(current_user)):
    if not user.is_authenticated:
        return RedirectResponse(url='/login')

@users.get('/channel/{username}', dependencies=[Depends(logged_in)])
def channel(*, username: str,
             request: Request,
            current_user: models.User = Depends(current_user),
            db: Session = Depends(get_db))
    do_something()

This works, but I have to put it in every single endpoint:
@users.get('/channel/{username}', dependencies=[Depends(logged_in)])

def channel(*, username: str,
             request: Request,
            current_user: models.User = Depends(current_user),
            db: Session = Depends(get_db)):
     
     if not user.is_authenticated:
            return RedirectResponse(url='/login')
@philmade philmade added the question Question or problem label Feb 26, 2020
@philmade
Copy link
Author

philmade commented Feb 26, 2020

A guy on Gitter helped me with this. The trick is to not return the Redirect - just call it. Like this:

def logged_in(user: models.User = Depends(current_user)):
    if not user.is_authenticated:
        RedirectResponse(url='/login')

@phy25
Copy link

phy25 commented Feb 26, 2020

I doubt if this works at all. At least the following does not work for me.

from fastapi import FastAPI, Depends
from starlette.responses import RedirectResponse

app = FastAPI()

def redirect() -> bool:
    RedirectResponse(url='/login')
    return True

@app.get('/')
def index(should_redirect: bool = Depends(redirect)):
    return should_redirect

What I did before is to use custom exception handler:

from fastapi import FastAPI, Depends
from starlette.requests import Request
from starlette.responses import RedirectResponse, Response

app = FastAPI()

class RequiresLoginException(Exception):
    pass

async def redirect() -> bool:
    raise RequiresLoginException

@app.exception_handler(RequiresLoginException)
async def exception_handler(request: Request, exc: RequiresLoginException) -> Response:
    return RedirectResponse(url='/login')

@app.get('/')
async def index(should_redirect: bool = Depends(redirect)) -> bool:
    return should_redirect

@philmade
Copy link
Author

Yeah you're correct - I had thought that it worked but it does not. Without specifically referencing the dependency in the endpoint, I can't easily get a redirect to happen at the dependency level, it only will happen at the endpoint level.

Anyone got an idea on how to force a redirect?

@phy25
Copy link

phy25 commented Feb 27, 2020

@NomeChomsky does my exception-based solution work for you?

@philmade
Copy link
Author

Hey I just tried that. It does work. I didn't fully understand it - so the Exception is somehow allowing a return statement to be triggered, but an actual return statement won't get triggered?

Its a good workaround. Would be good to be able to return / exectute a Redirect without a custom exception.

thank you for your help!

:)

@tiangolo
Copy link
Member

Thanks for the help here @phy25 ! 🍰 👏

@NomeChomsky yeah, just what @phy25 said ☝️

@github-actions
Copy link
Contributor

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

@bsatts
Copy link

bsatts commented Jul 8, 2020

Is there a way to define exception handlers on routers? There's currently no way to @router.exception_handler

@tiangolo
Copy link
Member

tiangolo commented Dec 6, 2020

Yeah, that's currently not supported @bsatts, only in the main app.

@eddyizm
Copy link

eddyizm commented Jan 3, 2022

@NomeChomsky how did you implement that example to your user authentication? I am having the same issue but not sure how that got sorted out.

@fboerman
Copy link

fboerman commented Jan 23, 2022

@NomeChomsky @eddyizm I would also like to know how I can implement this. I want my dependency to trigger a redirect when it cant find a certain cookie. I cant find in the docs a proper way to implement this without also thouching the main app source code.

@phy25 solution is therefor not possible for me

@gavrilovne
Copy link

gavrilovne commented Feb 17, 2022

@NomeChomsky @eddyizm I would also like to know how I can implement this. I want my dependency to trigger a redirect when it cant find a certain cookie. I cant find in the docs a proper way to implement this without also thouching the main app source code.

@phy25 solution is therefor not possible for me

Hi!
Try it

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)


async def check_auth(token: str = Depends(oauth2_scheme)):
    if not token:
        raise HTTPException(
            status_code=status.HTTP_307_TEMPORARY_REDIRECT,
            headers={'Location': '/login'})
    return True


@router.get("/login")
async def login(request: Request):
    return auth_template.TemplateResponse("login.html", {"request": request})


@router.get("/test")
async def test(auth: bool = Depends(check_auth)):
    return "OK"

screen

@fboerman
Copy link

@NomeChomsky @eddyizm I would also like to know how I can implement this. I want my dependency to trigger a redirect when it cant find a certain cookie. I cant find in the docs a proper way to implement this without also thouching the main app source code.
@phy25 solution is therefor not possible for me

Hi! Try it

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)


async def check_auth(token: str = Depends(oauth2_scheme)):
    if not token:
        raise HTTPException(
            status_code=status.HTTP_307_TEMPORARY_REDIRECT,
            headers={'Location': '/login'})
    return True


@router.get("/login")
async def login(request: Request):
    return auth_template.TemplateResponse("login.html", {"request": request})


@router.get("/test")
async def test(auth: bool = Depends(check_auth)):
    return "OK"

screen

thanks @gavrilovne ! Thanks works perfectly!

@tiangolo tiangolo changed the title [QUESTION] Returning a RedirectResponse in a dependency Returning a RedirectResponse in a dependency Feb 24, 2023
@tiangolo tiangolo reopened this Feb 28, 2023
@fastapi fastapi locked and limited conversation to collaborators Feb 28, 2023
@tiangolo tiangolo converted this issue into discussion #7817 Feb 28, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
question Question or problem question-migrate
Projects
None yet
Development

No branches or pull requests

7 participants