Is there any way to change the default response from Pydantic so that "msg"
is "message"
?
{
"detail": [
{
"loc": [
"body",
"password"
],
"msg": "Password should at least 8 characters long.",
"type": "value_error"
}
]
}
That looks like a JSON response and Pydantic by itself does not give out a JSON response for ValidationError
's. It should just be a regular raise
-d Exception
like this:
In [2]: from pydantic import BaseModel, constr
In [3]: class Credentials(BaseModel):
...: password: constr(min_length=8)
...:
In [4]: Credentials(password="xyz")
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 Credentials(password="xyz")
...
ValidationError: 1 validation error for Credentials
password
ensure this value has at least 8 characters (type=value_error.any_str.min_length; limit_value=8)
I think you are using FastAPI ( which has Pydantic integration ) and this JSON response is actually FastAPI's built-in error response when the request has ValidationError
's, as described in the FastAPI docs on Handling Errors . I can replicate a similar error format with this example:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel, constr
class Credentials(BaseModel):
password: constr(min_length=8)
app = FastAPI()
@app.post("/login")
async def login(credentials: Credentials):
print(credentials) # This is just as an example!
return JSONResponse(status_code=200)
$ curl -s --header "Content-Type: application/json" --request POST --data '{"password":"xyz"}' http://localhost:8000/login | jq
{
"detail": [
{
"loc": [
"body",
"password"
],
"msg": "ensure this value has at least 8 characters",
"type": "value_error.any_str.min_length",
"ctx": {
"limit_value": 8
}
}
]
}
To change the response body, check the FastAPI docs on Use the RequestValidationError body that shows you can access the default detail
list of errors, which you can then edit or copy over to a customized details list, and then set it as the content
of the JSON error response.
from fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel, constr
class Credentials(BaseModel):
password: constr(min_length=8)
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
# Get the original 'detail' list of errors
details = exc.errors()
modified_details = []
# Replace 'msg' with 'message' for each error
for error in details:
modified_details.append(
{
"loc": error["loc"],
"message": error["msg"],
"type": error["type"],
}
)
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": modified_details}),
)
@app.post("/login")
async def login(credentials: Credentials):
print(credentials) # Just as an example!
return JSONResponse(status_code=200)
$ curl -s --header "Content-Type: application/json" --request POST --data '{"password":"xyz"}' http://localhost:8000/login | jq
{
"detail": [
{
"loc": [
"body",
"password"
],
"message": "ensure this value has at least 8 characters",
"type": "value_error.any_str.min_length"
}
]
}
Of course, you can also simply just define your own custom content
and JSONResponse
.
Building upon the previous answer , you can use a validation exception handler , as shown below:
from fastapi import Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
for error in exc.errors():
error['message'] = error.pop('msg')
return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"detail": exc.errors()}))
A decorator is just a function which wraps around another function.
If you are using the @validate decorator, I find it that its easiest to make another decorator and place it above the original one. The new decorator can reformat the output.
In the example below I am using flask and flask-pydantic which is very comparable to fast-api.
from flask import Flask, request, make_response, jsonify
from flask_pydantic import validate
from pydantic import BaseModel
from typing import Optional
from functools import wraps
app = Flask(__name__)
def custom_error(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
res = fn(*args, **kwargs)
err_json = res.get_json()
# check if pydantic throws an error (if not continue normally)
if (err_json is None) or ("validation_error" not in err_json):
return res
val_err = err_json["validation_error"]
# here you implement the logic to change the msg format
if "body_params" in val_err:
final_msg = []
for sing_err in val_err["body_params"]:
final_msg.append(" ".join([sing_err["loc"][0], sing_err["msg"]]))
final_msg = ". ".join(final_msg)
# this is the final response format
return make_response(jsonify({
"instance": request.path,
"errors": {
"code": 'FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE',
"reason": final_msg}
}), 400)
return wrapper
class ExampleValidator(BaseModel):
some_int: int
some_str: str
opt_int: Optional[int]
@app.route("/test_end", methods=["POST"])
@custom_error
@validate(body=ExampleValidator)
def test_end():
return make_response('Success', 200)
1- Add Config class to the schema that inheritance from BaseModel.
2- In Config add value_msg_templates
dict and start to customize messages: key
is error type and value
is your new messages for this type.
class UserAuth(BaseModel):
email: EmailStr = Field(..., description="user email", example="userTest@example.com",)
username: str = Field(..., min_length=5, max_length=10, description="user username", example="userTest",)
password: str = Field(..., description="user password", example="Tyu*&^54",)
class Config:
error_msg_templates = {
'value_error.email': 'email address is not valid.',
}
where is types:
3- add these lines to main.py or app.py file:
from fastapi import FastAPI, Request, status, HTTPException, Response
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError
from pydantic.error_wrappers import ErrorWrapper
.
.
.
app = FastAPI()
.
.
.
@app.exception_handler(RequestValidationError)
async def http_exception_accept_handler(request: Request, exc: RequestValidationError) -> Response:
raw_errors = exc.raw_errors
error_wrapper: ErrorWrapper = raw_errors[0]
validation_error: ValidationError = error_wrapper.exc
overwritten_errors = validation_error.errors()
return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={"detail": jsonable_encoder(overwritten_errors)},
)
.
.
.
Finish:)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.