[docs]classEndpointError(Exception):""" To be raised by endpoints when a foreseen error occurs. This exception doesn't cause a client-side crash dialog. :param inner: inner exception :param message: message """def__init__(self,inner,message=None):Exception.__init__(self)self.inner=innerself.message=messageorstr(inner)try:self.traceback_str=traceback.format_exc()exceptExceptionase:self.traceback_str=Nonedef__str__(self):returnself.message
[docs]classEndpointReturn(Exception):""" Raising ``EndpointReturn`` will return a custom HTTP code in the API endpoints. :param code: HTTP code :param data: response data """def__init__(self,code,data=None):Exception.__init__(self)self.code=codeself.data=datadef__str__(self):returnf'[EndpointReturn: {self.code}]'
[docs]defendpoint(page=False,api=False,auth=True):""" It's recommended to decorate all HTTP handling methods with ``@endpoint``. ``@endpoint(auth=True)`` will require authenticated session before giving control to the handler. ``@endpoint(api=True)`` will wrap responses and exceptions into JSON, and will also provide special handling of :class:`EndpointsError` :param auth: requires authentication for this endpoint :type auth: bool :param page: enables page mode :type page: bool :param api: enables API mode :type api: bool """defdecorator(fx):fx.api=apifx.auth=authfx.page=page@wraps(fx)defwrapper(self,context,*args,**kwargs):ifauthandnotself.context.identity:context.respond_unauthenticated()ifcontext.env['PATH_INFO'].startswith('/webdav/'):context.add_header("WWW-Authenticate",'Basic realm="Ajenti", charset="UTF-8"')return''status=200try:result=fx(self,context,*args,**kwargs)ifisinstance(context.status,str):# Catch 404 from api and propagateif'200'notincontext.status:status=context.statusifpage:returnresultexceptEndpointReturnase:logging.debug(f'Endpoint return at {context.path}: {e.code}')status=e.coderesult=e.dataexcept(EndpointError,SecurityError)ase:logging.warning(f'Endpoint error at {context.path}: {e.message}')ifpage:raisestatus=500result={'message':str(e.message),'exception':str(e.__class__.__name__),'traceback':str(getattr(e,'traceback_str','')),}# pylint: disable=W0703exceptExceptionase:logging.error(f'Unhandled endpoint error at {context.path}')traceback.print_exc()ifpage:raisestatus=500result={'message':str(e),'exception':str(e.__class__.__name__),'traceback':str(traceback.format_exc()),}ifapi:context.add_header('Content-Type','application/json')context.respond(status)returnjson.dumps(result)# In case of other response, like e.g. XML# Currently available if api=False and page=Falsereturnresultreturnwrapperreturndecorator