11"""Structured JSON error responses for API routes."""
22
3- from flask import jsonify , make_response , request
3+ from flask import current_app , jsonify , request
44from marshmallow import ValidationError as MarshmallowValidationError
55from sqlalchemy .exc import SQLAlchemyError
66
@@ -101,6 +101,7 @@ def handle_429(error):
101101@mod_api .errorhandler (500 )
102102def handle_500 (error ):
103103 """Handle unexpected server errors for API routes."""
104+ current_app .logger .exception (error )
104105 return make_error_response (
105106 'internal_error' ,
106107 'An unexpected error occurred.' ,
@@ -122,27 +123,42 @@ def handle_marshmallow_validation_error(error):
122123@mod_api .errorhandler (SQLAlchemyError )
123124def handle_sqlalchemy_error (error ):
124125 """Log database errors."""
125- from flask import g
126- log = getattr (g , 'log' , None )
127- if log :
128- log .error (f'Database error in API: { type (error ).__name__ } ' )
126+ current_app .logger .exception (error )
129127 return make_error_response (
130128 'internal_error' ,
131129 'An unexpected database error occurred.' ,
132130 http_status = 500 ,
133131 )
134132
135133
136- @mod_api .after_app_request
134+ @mod_api .errorhandler (ValueError )
135+ def handle_value_error (error ):
136+ """Catch plain ValueErrors raised by model @validates (e.g. scopes_json)."""
137+ return make_error_response (
138+ 'invalid_input' ,
139+ str (error ),
140+ http_status = 400 ,
141+ )
142+
143+
137144def convert_api_errors_to_json (response ):
138145 """Catch routing errors that were handled by global app handlers and convert them to JSON."""
139146 if request .path .startswith (_API_PREFIX ):
140147 if response .status_code >= 500 :
141- return make_error_response (
148+ new_resp = make_error_response (
142149 'internal_error' , 'An unexpected error occurred.' , http_status = response .status_code
143150 )
151+ response .data = new_resp .data
152+ response .mimetype = new_resp .mimetype
153+ return response
144154 if response .status_code == 404 :
145- return make_error_response ('not_found' , 'Resource not found.' , http_status = 404 )
155+ new_resp = make_error_response ('not_found' , 'Resource not found.' , http_status = 404 )
156+ response .data = new_resp .data
157+ response .mimetype = new_resp .mimetype
158+ return response
146159 if response .status_code == 405 :
147- return make_error_response ('method_not_allowed' , 'Method not allowed.' , http_status = 405 )
160+ new_resp = make_error_response ('method_not_allowed' , 'Method not allowed.' , http_status = 405 )
161+ response .data = new_resp .data
162+ response .mimetype = new_resp .mimetype
163+ return response
148164 return response
0 commit comments