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
Allow users to raise RequestValidationError #471
Comments
This would be the simplest possible solution: class RequestValidationError(HTTPException):
def __init__(self, loc, msg, typ):
super().__init__(422, [{'loc': loc, 'msg': msg, 'type': typ}]) Passing It is also not always obvious what to use for the |
Those validations come directly from Pydantic. So you could create a Pydantic model with a custom validator (with your custom logic), and validate your data with Pydantic, checking for validation errors, and extracting those errors. For example: from fastapi import FastAPI, HTTPException
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, validator, ValidationError
class CondaChannel(BaseModel):
channel: str
@validator("channel")
def channel_validator(cls, v: str):
# Do some validation
if not v.startswith("superawesomechannel"):
raise ValueError("Only super awesome channels are supported.")
return v
app = FastAPI()
@app.post("/upload/{channel}")
async def conda_upload(channel: str):
try:
CondaChannel(channel=channel)
except ValidationError as err:
raise HTTPException(status_code=422, detail=jsonable_encoder(err.errors())) |
Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues. |
The {
"detail": [
{
"loc": [
"path",
"channel"
],
"msg": "Channel 'foo' does not match some condition",
"type": "value_error.str.condition"
}
]
} Instead, you will get: {
"detail": [
{
"loc": [
"channel"
],
"msg": "Only super awesome channels are supported.",
"type": "value_error"
}
]
} |
Just to chime in on this old issue.. it does seem like something is missing here for the use case where not everything fits neatly into pydantic model based validation. A simple example is where the validation error doesn't come from your own validation logic, but instead from the response of a 3rd party API. In that case, all you know is the field that had the issue and the validation error message and you want to feed that into the standard error response flow. |
Something I did recently to add validation to an endpoint pending deprecation, that didn't have pydantic validation: async def my_deprecated_endpoint(request: Request, data_as_dict: dict):
try:
# Reshape the data received using pydantic models
data = DataAsModel(something=data_as_dict["a_nested_field"]["something"])
except ValidationError as e:
raise RequestValidationError(errors=e.raw_errors) # This is the key piece
# Rest of the logic for my endpoint... Note that in my case it's safe to show to the client any error that happens when trying to format my data with the pydantic model. I would discourage having a wide |
response error (although not exactly the same) - See tiangolo/fastapi#471
I needed some custom validation for a query parameter that I split on comma and check if its on an Enum. from pydantic.error_wrappers import ErrorWrapper
try:
parts = [enum_type[s.upper()].value for s in value.split(",")]
except KeyError as e:
en = enum_type.__name__.lower()
err = f"Invalid {en} value: {str(e).lower()}"
raise RequestValidationError([ErrorWrapper(ValueError(err), ("query", en))]) The error response with status 422 comes out like this {
"detail": [
{
"loc": [
"query",
"status"
],
"msg": "Invalid status value: 'foo'",
"type": "value_error"
}
]
} |
Combining some of the suggestions here I landed on this generic solution, that still works with OpenAPI by passing the parameter name as T = TypeVar("T")
def query_list_parser(
parameter_name: str,
inner_type: type[T],
) -> Callable[[str], Awaitable[list[T]]]:
async def parse_list(
parameter: str = Query(Required, alias=parameter_name),
) -> list[T]:
def parse() -> Iterator[T]:
for index, value in enumerate(parameter.split(",")):
try:
yield inner_type(value)
except (TypeError, ValueError) as exception:
raise RequestValidationError(
[ErrorWrapper(exception, ("query", parameter_name, index))]
)
return list(parse())
return parse_list
@router.get("/")
async def view(ids: list[ID] = Depends(query_list_parser("ids", ID))) -> Response:
... |
Hello everyone! I'm needed validating date by
Code example: class DateTimeError(PydanticValueError):
code = 'date.not_gt'
msg_template = 'start date must be greater than 2020-07-09'
def _start_date(start_date: datetime.date):
if start_date < datetime.date(2020, 7, 29):
raise RequestValidationError(errors=[
ErrorWrapper(
DateTimeError(limit_value='2020-07-29'),
loc=('query', 'start_date'),
),
])
return start_date
@router.get('/graph-data/')
async def get_users_count_graph_data(
start_date: datetime.date = Depends(_start_date),
finish_date: datetime.date = None,
):
... Invalid HTTP/1.1 422 Unprocessable Entity
{
"detail": [
{
"ctx": {
"limit_value": "2020-07-29"
},
"loc": [
"query",
"start_date"
],
"msg": "start date must be greater than 2020-07-09",
"type": "value_error.date.not_gt"
}
]
} I'm hope that this method can help you. Thanks! |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Sometimes, you have to perform additional validation of parameters (e.g., Path parameters) inside your API function. Currently, it is not very convenient to produce an error structured like the ones that the internal validation produces (e.g. when an int-typed param cannot be parsed).
Describe the solution you'd like
Describe alternatives you've considered
You can explicitly construct the JSON structure. But you need to remember the correct responsec code and the correct JSON structure:
Additional context
Add any other context or screenshots about the feature request here.
The text was updated successfully, but these errors were encountered: