Skip to content

Commit f25b6de

Browse files
authored
Raise 400 error on invalid cursor value (#957)
1 parent 6f5268a commit f25b6de

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

fastapi_pagination/cursor.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"CursorParams",
66
]
77

8+
import binascii
89
from base64 import b64decode, b64encode
910
from typing import (
1011
Any,
@@ -17,7 +18,7 @@
1718
)
1819
from urllib.parse import quote, unquote
1920

20-
from fastapi import Query
21+
from fastapi import HTTPException, Query, status
2122
from pydantic import BaseModel, Field
2223
from typing_extensions import Literal
2324

@@ -45,8 +46,14 @@ def decode_cursor(cursor: Optional[str], *, to_str: bool) -> Optional[Cursor]:
4546

4647
def decode_cursor(cursor: Optional[str], *, to_str: bool = True) -> Optional[Cursor]:
4748
if cursor:
48-
res = b64decode(unquote(cursor).encode())
49-
return res.decode() if to_str else res
49+
try:
50+
res = b64decode(unquote(cursor).encode())
51+
return res.decode() if to_str else res
52+
except binascii.Error:
53+
raise HTTPException(
54+
status_code=status.HTTP_400_BAD_REQUEST,
55+
detail="Invalid cursor value",
56+
) from None
5057

5158
return None
5259

tests/test_cursor.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
1+
from fastapi import FastAPI, status
2+
from fastapi.testclient import TestClient
13
from pytest import raises
24

3-
from fastapi_pagination import paginate
4-
from fastapi_pagination.cursor import CursorParams
5+
from fastapi_pagination import add_pagination, paginate, resolve_params
6+
from fastapi_pagination.cursor import CursorPage, CursorParams
57

68

79
def test_unsupported_params():
810
with raises(ValueError, match="^'cursor' params not supported$"):
911
paginate([1, 2, 3], CursorParams())
12+
13+
14+
def test_invalid_cursor():
15+
app = FastAPI()
16+
client = TestClient(app)
17+
18+
@app.get(
19+
"/route",
20+
response_model=CursorPage[int],
21+
)
22+
def route():
23+
params = resolve_params()
24+
params.to_raw_params()
25+
26+
return CursorPage(items=[])
27+
28+
add_pagination(app)
29+
30+
response = client.get("/route", params={"cursor": "invalid"})
31+
32+
assert response.status_code == status.HTTP_400_BAD_REQUEST
33+
assert response.json() == {"detail": "Invalid cursor value"}

0 commit comments

Comments
 (0)