How to return jsonp with cherrypy? I am currently using the following to return json:
class Root(object):
#cherrypy.expose
#tools.json_out
def index(self):
return {'key':'value'}
Is there some annotation for jsonp?
Check out this implementation...
jsonp
Hope this helps,
Andrew
Related
Python version 3.9, FastAPI version 0.78.0
I have a custom function that I use for application exception handling. When requests run into internal logic problems, i.e I want to send an HTTP response of 400 for some reason, I call a utility function.
#staticmethod
def raise_error(error: str, code: int) -> None:
logger.error(error)
raise HTTPException(status_code=code, detail=error)
Not a fan of this approach. So I look at
from fastapi import FastAPI, HTTPException, status
from fastapi.respones import JSONResponse
class ExceptionCustom(HTTPException):
pass
def exception_404_handler(request: Request, exc: HTTPException):
return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": "404"})
app.add_exception_handler(ExceptionCustom, exception_404_handler)
The problem I run into with the above approach is the inability to pass in the message as an argument.
Any thoughts on the whole topic?
Your custom exception can have any custom attributes that you want. Let's say you write it this way:
class ExceptionCustom(HTTPException):
pass
in your custom handler, you can do something like
def exception_404_handler(request: Request, exc: HTTPException):
return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": exc.detail})
Then, all you need to do is to raise the exception this way:
raise ExceptionCustom(status_code=404, detail='error message')
Note that you are creating a handler for this specific ExceptionCustom. If all you need is the message, you can write something more generic:
class MyHTTPException(HTTPException):
pass
def my_http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(status_code=exc.status_code, content={"message": exc.detail})
app.add_exception_handler(MyHTTPException, my_http_exception_handler)
This way you can raise any exception, with any status code and any message and have the message in your JSON response.
There's a detailed explanation on FastAPI docs
You can add custom exception handlers, and use attributes in your Exception class (i.e., class MyException(Exception) in the example below) to pass any message/variables that you would like to do so. The exception handler (i.e., #app.exception_handler(MyException) in the case below) will handle the exception as you wish and return your custom message. For more options, please have a look at this related answer as well.
Working Example
To trigger the exception in the example below, access the following URL from your browser: http://localhost:8000/something
from fastapi import FastAPI, Request, status
from fastapi.responses import JSONResponse
class MyException(Exception):
def __init__(self, name: str):
self.name = name
app = FastAPI()
#app.exception_handler(MyException)
async def my_exception_handler(request: Request, exc: MyException):
return JSONResponse(status_code=status.HTTP_404_NOT_FOUND,
content={"message": f"{exc.name} cannot be found." })
#app.get("/{name}")
def read_name(name: str):
if name == "something":
raise MyException(name=name)
return {"name": name}
When I try to use Django Rest Framework extra actions on a viewset, I can not make the decorator's serializer_class work.
class ClientViewSet(ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
def get_queryset(self):
# Do things
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return self.serializer_class
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
queryset = get_object_or_404(Client, name__iexact=name)
serializer = self.get_serializer(queryset)
return Response(serializer.data)
So, even if the extra action is supposedly overriding the ViewSet default serializer class, I still get ClientSerializer instead of ClientDetailSerializer.
The official documentation states that...
The decorator allows you to override any viewset-level configuration such as permission_classes, serializer_class, filter_backends...:
My get_serializer_class override defaults to the ViewSet serializer_class attribute for my extra actions. If I understand correctly, this is basically what GenericAPIView get_serializer_class does under the hood:
def get_serializer_class(self):
"""
(...)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
I guess I'm missing something obvious here. Just can not figure out what...
Any help is appreciated. Thanks in advance :)
Why not use it like this? I'm guessing you're doing something wrong in get_serializer_class.
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
object = get_object_or_404(Client, name__iexact=name)
serializer = ClientDetailSerializer(object)
return Response(serializer.data)
When you override the get_serializer_class without calling the super of this class, the super class doesn't run.
user this:
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return super().get_serializer_class()
I am trying to catch unhandled exceptions at global level. So somewhere in main.py file I have the below:
#app.exception_handler(Exception)
async def exception_callback(request: Request, exc: Exception):
logger.error(exc.detail)
But the above method is never executed. However, if I write a custom exception and try to catch it (as shown below), it works just fine.
class MyException(Exception):
#some code
#app.exception_handler(MyException)
async def exception_callback(request: Request, exc: MyException):
logger.error(exc.detail)
I have gone through Catch exception type of Exception and process body request #575. But this bug talks about accessing request body. After seeing this bug, I feel it should be possible to catch Exception.
FastAPI version I am using is: fastapi>=0.52.0.
Thanks in advance :)
Update
There are multiple answers, I am thankful to all the readers and authors here.
I was revisiting this solution in my application. Now I see that I needed to set debug=False, default it's False, but I had it set to True in
server = FastAPI(
title=app_settings.PROJECT_NAME,
version=app_settings.VERSION,
)
It seems that I missed it when #iedmrc commented on answer given by #Kavindu Dodanduwa.
In case you want to capture all unhandled exceptions (internal server error), there's a very simple way of doing it. Documentation
from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import Response
app = FastAPI()
async def catch_exceptions_middleware(request: Request, call_next):
try:
return await call_next(request)
except Exception:
# you probably want some kind of logging here
return Response("Internal server error", status_code=500)
app.middleware('http')(catch_exceptions_middleware)
Make sure you place this middleware before everything else.
You can do something like this. It should return a json object with your custom error message also works in debugger mode.
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
#app.exception_handler(Exception)
async def validation_exception_handler(request, err):
base_error_message = f"Failed to execute: {request.method}: {request.url}"
# Change here to LOGGER
return JSONResponse(status_code=400, content={"message": f"{base_error_message}. Detail: {err}"})
Adding a custom APIRoute can be also be used to handle global exceptions. The advantage of this approach is that if a http exception is raised from the custom route it will be handled by default Starlette's error handlers:
from typing import Callable
from fastapi import Request, Response, HTTPException, APIRouter, FastAPI
from fastapi.routing import APIRoute
from .logging import logger
class RouteErrorHandler(APIRoute):
"""Custom APIRoute that handles application errors and exceptions"""
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
try:
return await original_route_handler(request)
except Exception as ex:
if isinstance(ex, HTTPException):
raise ex
logger.exception("uncaught error")
# wrap error into pretty 500 exception
raise HTTPException(status_code=500, detail=str(ex))
return custom_route_handler
router = APIRouter(route_class=RouteErrorHandler)
app = FastAPI()
app.include_router(router)
Worked for me with fastapi==0.68.1.
More on custom routes: https://fastapi.tiangolo.com/advanced/custom-request-and-route/
It is a known issue on the Fastapi and Starlette.
I am trying to capture the StarletteHTTPException globally by a following simple sample.
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import JSONResponse
app = FastAPI()
#app.exception_handler(StarletteHTTPException)
async def exception_callback(request: Request, exc: Exception):
print("test")
return JSONResponse({"detail": "test_error"}, status_code=500)
if __name__ == "__main__":
uvicorn.run("test:app", host="0.0.0.0", port=1111, reload=True)
It works. I open the browser and call the endpoint / and try to access http://127.0.0.1:1111/ , it will return the json {"detail":"test_error"} with HTTP code "500 Internal Server Error" .
However, when I only changed StarletteHTTPException to Exception in the #app.exception_handler,
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import JSONResponse
app = FastAPI()
#app.exception_handler(Exception)
async def exception_callback(request: Request, exc: Exception):
print("test")
return JSONResponse({"detail": "test_error"}, status_code=500)
if __name__ == "__main__":
uvicorn.run("test:app", host="0.0.0.0", port=1111, reload=True)
The method exception_callback could not capture the StarletteHTTPException when I accessed the http://127.0.0.1:1111/ . It reported 404 error.
The excepted behaviour should be: StarletteHTTPException error could be captured by the method exception_handler decorated by Exception because StarletteHTTPException is the child class of Exception.
However, it is a known issue reported in Fastapi and Starlette
https://github.com/tiangolo/fastapi/issues/2750
https://github.com/tiangolo/fastapi/issues/2683
https://github.com/encode/starlette/issues/1129
So we are not able to acheieve the goal currently.
First I invite to get familiar with exception base classes in python. You can read them in the document Built-in Exceptions
Secondly, read through fastApi default exception overriding behaviour Override the default exception handlers
What you must understand is that #app.exception_handler accepts any Exception or child classes derived from Exception. For example RequestValidationError is a subclass of python built in ValueError which itself a subclass of Exception.
So you must design your own exceptions or throw available exceptions with this background. I guess what went wrong is with your logger logger.error(exc.detail) by either not having a detail field or not having a proper logger configuration.
Sample code :
#app.get("/")
def read_root(response: Response):
raise ArithmeticError("Divide by zero")
#app.exception_handler(Exception)
async def validation_exception_handler(request, exc):
print(str(exc))
return PlainTextResponse("Something went wrong", status_code=400)
Output :
A stdout entry and a response with Something went wrong
I found a way to catch exceptions without the "Exception in ASGI application_" by using a middleware. Not sure if this has some other side effect but for me that works fine! #iedmrc
#app.middleware("http")
async def exception_handling(request: Request, call_next):
try:
return await call_next(request)
except Exception as exc:
log.error("Do some logging here")
return JSONResponse(status_code=500, content="some content")
I was searching for global handler for fast api for giving custome message for 429 status code i found and implemented, working fine for me
#app.exception_handler(429)
async def ratelimit_handler(request: Request, exc: Exception):
return JSONResponse({'message': "You have exceeded your request quota. Kindly try after some time.", 'status': 'failed'})
I have basic twister app and i keep getting errors like that:
Request did not return bytes
Request:
Resource:
<main.MainPageDispatcher object at 0x7f049fa62be0>
Value:
'hello'
Everywhere, even in official docs' examples I see that string is returned and yet it not works for me. If I comment out first return and send bytes instead of string it is working.
Can anyone help me understand how it works? If it has to be in bytes then why official guides are returning strings?
My code:
from twisted.web.server import Site
from twisted.web.static import File
from twisted.web.resource import Resource
from twisted.internet import reactor
class MainPageDispatcher(Resource):
isLeaf = True
def __init__(self):
super().__init__()
def render_GET(self, request):
request.setHeader(b"content-type", b"text/html")
return "hello"
return bytes("hello", "utf-8")
root = MainPageDispatcher()
factory = Site(root)
reactor.listenTCP(8888, factory)
reactor.run()
In python3 I'm using:
def render_GET(self, request):
request.setHeader("Content-Type", "text/html; charset=utf-8")
return "<html>Hello, world!</html>".encode('utf-8')
str.encode('utf-8') returns a bytes representation of the Unicode string
I have seen the documentation but i am not able to make it working.If anyone could explain me more clearly how to create a custom 403 page in pyramid,it would be great
Thanks
You can add a renderer to your #forbidden_view_config() decorator:
#forbidden_view_config(renderer='myforbiddentemplate.mako')
Or, you could probably also do some sort of custom views, too:
from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
HTTPForbidden,
HTTPBadRequest,
HTTPInternalServerError
)
#view_config(context=HTTPNotFound, renderer='HTTPNotFoundPage.mako')
def my_view(request):
Just decore your view with forbidden_view_config:
#view_config(route_name='login', renderer='login-page.mako')
#forbidden_view_config(renderer='login-page.mako')
def login_view(request):
return {}
from pyramid.view import forbidden_view_config
#forbidden_view_config(renderer='your_template_name')
def forbidden_view(request):
request.response.status = 403
return {}