-
Notifications
You must be signed in to change notification settings - Fork 411
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
Feature request: HTTP response of an unsuccessful response validation must be a server-side error #5888
Comments
Hi @amin-farjadi! Thanks for opening this issue! This is actually not a bug, it's what you can expect when the request/response is not validated as expected, so you'll get an HTTP 422. While this is expected behavior, I'm open to accepting a PR implementing a default replacement for 422 for any other HTTP StatusCode when working with data validation. Are you interested in submitting this PR to implement this? If you want to change this behavior using current features, you can intercept Please take a look in this and see if works for you: from http import HTTPStatus
from typing import Literal
from aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.api_gateway import RequestValidationError
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response, content_types
from pydantic import BaseModel
app = APIGatewayRestResolver(enable_validation=True)
class Todo(BaseModel):
title: str
status: Literal["Pending", "Done"]
@app.exception_handler(RequestValidationError)
def handle_validation_error(ex: RequestValidationError):
return Response(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
content_type=content_types.APPLICATION_JSON,
body={"msg": ex.errors()}
)
@app.get("/non_empty_string")
def return_non_empty_string() -> Todo:
return "hello"
@app.get("/non_conforming_dict")
def return_non_conforming_dict() -> Todo:
return {"title": "fix_response_validation"}
def lambda_handler(event, context):
return app.resolve(event, context) |
Hi @leandrodamascena, yeah happy to submit a PR. I understand and agree with the 422 http response when client payload cannot be validated. But, just think that if, on the backend we are putting together some data and, somehow those data end up not adhering to the anticipated structure that is a backend (server side) issue and the status code should reflect that. Am I unsure what the best status code for it would be—a 500 might be good. |
I still think this is a client error of a validation/serialization when we talk about request validation. Pydantic models/dataclasses should validate the structure of a given model, not do validation on the data itself, I mean, they should validate if a string is a string, not if a string is a string and exists in the database. If I have an API that expects a JSON body of In case of talking about response validation, I see cases where you annotate the function to return a float, but for some reason it is returning a string, so it would be an HTTP 500 yes if the customer wants.
I think we can leave it open for the customers to decide. We keep 422 and the default response, but the customer can decide the best error code for that specific resolver or route. I'm just not sure about the experience, I don't know if it's the best Configure at route levelfrom aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver
app = APIGatewayRestResolver(enable_validation=True)
@app.get("/none", error_validation_http_code=500)
def return_x() -> int:
return "a" Configure at resolver levelfrom aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver
app = APIGatewayRestResolver(enable_validation=True)
app.set_error_validation_http_code(status=500)
@app.get("/none")
def return_x() -> int:
return "a" Bothfrom aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver
app = APIGatewayRestResolver(enable_validation=True)
app.set_error_validation_http_code(status=500)
@app.get("/none", error_validation_http_code=400) # Will return 400
def return_x() -> int:
return "a"
@app.get("/none") # Will return 500
def return_y() -> int:
return "a" What do you think? |
Problem StatementSorry I might have been vague with my explanation. Say I have designed an API endpoint that has a POST method and accepts payload of type For simplicity say that our endpoint is designed to just act as a proxy to another SaaS provider. The SaaS provider's response body is of type So, we have: @app.post("/endpoint")
def proxy_saas(payload: PayloadModel) -> ResponseModel:
return requests.post("www.saas_provider.example", json=payload.model_dump()).json() # expected to be of type ResponseModel The issue then arises if the SaaS provider has a bad API and, out of nowhere, returns a ConfigurationThe above configuration looks like a great feature. However, I personally like to distinguish between the http status codes resulted by |
Hi @amin-farjadi, I see! Thanks for clarifying. We only have a problem if we replace I agree with your approach and it's a good improvement, but we need to find a way to keep the default behavior as WDYT? |
I agree that changing the |
Yes, but SerializationError is intended to be used when we cannot serialize the object because we don't know how to serialize it. For example SQLAlchemy models and others, where you need to bring your own custom serializer. What you are trying to achieve here is that you have a function annotation with a Pydantic model, but you are returning a
Yes, lets make this an optional behavior and change it to the default behavior in Powertools v4. Please let me know if you need any help to send this PR. We can work together to get it merged. |
Thank you for opening this issue. Since we haven't heard back, we'll be closing it for now. Please don't hesitate to open a new one or start a new discussion if you have any updates or further thoughts. We appreciate your input! |
|
Hi @leandrodamascena, @anafalcao Found some time and happy to implement this feature. Please open this issue if you could. |
Hey @amin-farjadi! Sure, go ahead! Please let me know if you need any help. Reopening this issue! |
Expected Behaviour
When:
enable_validation=True
and,Expected:
Current Behaviour
Code snippet
Possible Solution
_call_exception_handler
method ofAPIGatewayResolver
class returns a 422 response ifRequestValidationError
is raised. Change response from 422 to a more appropriate response—possibly a 500.Steps to Reproduce
Powertools for AWS Lambda (Python) version
latest, 3.4.1
AWS Lambda function runtime
3.12
Packaging format used
PyPi
Debugging logs
The text was updated successfully, but these errors were encountered: