You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generally having access to falcon's req and resp parameters is a nice feature to have when you want to read/manipulate headers or provide some custom per-request context value. Typical use case for per-request context variables are database session/connection objects that cannot be stored as resource instance attributes (needs to be unique for every request).
These are usually implemented as middlewares that are able to update req.context context dictionary. Unfortunately when using generic API resource classes (e.g. ListCreateAPI, PaginatedListAPI) user is expected to provide only basic resource manipulation methods like list(), retrieve(), create() etc. These methods accept only following set of arguments:
params: dictionary of deserialised keyword parameters (defined using resource-level param classes)
meta: dictionary with additional metadata that will be included in output response
validated (only on create/update handlers): deserialised resource representation provided by client in request body
**kwargs: additional keyword arguments retrieved from falcon's URI template associated to resource route.
Because basic resource manipulation methods (list/retrieve/create etc.) accept only specific set of arguments and none of them represents falcon.Request object. The only place where custom context can be provided is **kwargs dictionary that was primarily supposed to only hold keyword arguments retrieved from URI template.
Currently available approach - overriding HTTP method handlers
Right now, the simplest way to provide custom context to every request is to directly override falcon's HTTP method handlers in custom resource classes (inherited from generic and basic resources classes) and pass them further in **kwargs dictionary using super(). Example:
# assume Session is some db layer abstractiondefMyResource(ListCreateAPI):
defon_get(req, resp, **kwargs): # <= overrides default HTTP GET handlersuper().on_get(req, resp, db_session=Session(), **kwargs)
deflist(req,req, db_session, **kwargs): # <= called by super().on_get()returndb_session.all()
defon_post(req, resp, **kwargs): # <= overrides default HTTP POST handlersuper().on_post(req, resp, custom_var="some_value", **kwargs)
defcreate(req,req, validated, db_session, **kwargs): # <= called by super().on_patch()returndb_session.insert(validated)
Of course this is very impractical because every method handler that requires such additional context needs to be overridden. Also **kwargs are now only expected to hold only values from URI template and their purpose is documented exactly as URI template keyword values.
Currently available approach - using middleware/hooks
Overriding every HTTP method handler in exactly the same way is not a clean approach if specific context is required by every API endpoint. This will require a lot of redundant and messy code. One could reduce amount of boilerplate by providing custom base classes (based on graceful generics) with HTTP method handlers overridden to provide additional context. This will still require a lot of code duplication and will be hard to maintain in the long term.
In many frameworks (including falcon) the custom context is usually provided using two methods:
use global middleware that is able to provide custom context for every request/response in the application: it is fine if same context is needed in every API endpoint. Usually great for things like db sessions/connections, or user sessions.
use per-method or per-resource hooks: implemented in similar fashion to middleware but offer more fine-grained control and can be attached to specific resources or resource's HTTP methods (usually using decorators).
Both middleware and hooks from falcon can be used in graceful resources but their usage is very limited. The only parts of above features that could be used to provide context are:
process_resource(self, req, resp, resource, params) method of middleware class: the params dictionary is the save object unpacked as **kwargs keyword arguments. We cannot use anything else because req & resp are unavailable in basic resource manipulation handlers (list/create/retrieve etc.) and the resource instance can be shared between multiple worker threads.
action argument of falcon.before(action) decorator with signature of action(req, resp, resource, params): same as for middlewares -- only params parameter that translates directly to **kwargs can be used as a container for new context values.
Additionally usage of hooks in graceful is limited even further. They can be attached to whole resource:
But cannot be easily attached to specific resource manipulation method. The falcon.before() expects decorated function to have signature of a HTTP method handler (i.e. on_get(req, resp, **kwargs), on_post(req, resp, **kwargs) and so on). Due to this the only way to attach falcon.before hooks right now is through following boilerplate.
So it is too verbose and also counterintuitive. Note that compatibility with falcon hooks is another feature we would like to have so we could support any falcon contrib package that provides hooks. This anyway should be discussed as a separate issue (see #31 )
Summary
Existing ways of handling custom context values are too verbose, require too much boilerplate and generally exploit **kwargs dictionary of list/retrieve/update/create methods that has completely different purpose in both falcon and graceful.
I in my opinion the best approach would be to expose somehow the Request.context object in the basic resources. The best solution should:
Solve all the problems of existing approaches (preferably by supporting every method explained earlier)
Allow to provide context in the most convenient way to the user. So it should not favour any of the existing approaches.
Do not introduce any backwards incompatibility (best) or at least be an opt-in feature with only minor backwards incompatibility when toggled (still fine).
The text was updated successfully, but these errors were encountered:
compatible with existing techniques of providing context in falcon
compatible with any existing falcon extension built using hooks or middleware
Also if we improve support for falcon hooks (per-method falcon hooks with falcon.before) this solution will still work as expected.
Backward compatibility concerns:
Good practice is to allow passing any set of keyword arguments to resource manipulation methods using generic and basic resources in graceful. Anyway we cannot be sure that every developer uses this approach. We should expect that graceful users sometimes do following:
classMyResourceList(ListAPI):
deflist(params, meta):
pass# orclassMyResourceItem(RetrieveAPI):
defretrieve(params, meta, object_id): # <= object id from URI templatepass
We could ensure backwards compatibility by using class keyword arguments:
This is feature of Python 3 but we do not target anything below Python 3.3. Of course default for with_context for 0.x branch would be False and for 1.x would be True
Generally having access to falcon's
req
andresp
parameters is a nice feature to have when you want to read/manipulate headers or provide some custom per-request context value. Typical use case for per-request context variables are database session/connection objects that cannot be stored as resource instance attributes (needs to be unique for every request).These are usually implemented as middlewares that are able to update
req.context
context dictionary. Unfortunately when using generic API resource classes (e.g. ListCreateAPI, PaginatedListAPI) user is expected to provide only basic resource manipulation methods likelist()
,retrieve()
,create()
etc. These methods accept only following set of arguments:params
: dictionary of deserialised keyword parameters (defined using resource-level param classes)meta
: dictionary with additional metadata that will be included in output responsevalidated
(only on create/update handlers): deserialised resource representation provided by client in request body**kwargs
: additional keyword arguments retrieved from falcon's URI template associated to resource route.Because basic resource manipulation methods (list/retrieve/create etc.) accept only specific set of arguments and none of them represents falcon.Request object. The only place where custom context can be provided is
**kwargs
dictionary that was primarily supposed to only hold keyword arguments retrieved from URI template.Currently available approach - overriding HTTP method handlers
Right now, the simplest way to provide custom context to every request is to directly override falcon's HTTP method handlers in custom resource classes (inherited from generic and basic resources classes) and pass them further in
**kwargs
dictionary usingsuper()
. Example:Of course this is very impractical because every method handler that requires such additional context needs to be overridden. Also
**kwargs
are now only expected to hold only values from URI template and their purpose is documented exactly as URI template keyword values.Currently available approach - using middleware/hooks
Overriding every HTTP method handler in exactly the same way is not a clean approach if specific context is required by every API endpoint. This will require a lot of redundant and messy code. One could reduce amount of boilerplate by providing custom base classes (based on graceful generics) with HTTP method handlers overridden to provide additional context. This will still require a lot of code duplication and will be hard to maintain in the long term.
In many frameworks (including falcon) the custom context is usually provided using two methods:
Both middleware and hooks from falcon can be used in graceful resources but their usage is very limited. The only parts of above features that could be used to provide context are:
process_resource(self, req, resp, resource, params)
method of middleware class: theparams
dictionary is the save object unpacked as**kwargs
keyword arguments. We cannot use anything else becausereq
&resp
are unavailable in basic resource manipulation handlers (list/create/retrieve etc.) and theresource
instance can be shared between multiple worker threads.action
argument offalcon.before(action)
decorator with signature ofaction(req, resp, resource, params)
: same as for middlewares -- onlyparams
parameter that translates directly to**kwargs
can be used as a container for new context values.Additionally usage of hooks in graceful is limited even further. They can be attached to whole resource:
But cannot be easily attached to specific resource manipulation method. The
falcon.before()
expects decorated function to have signature of a HTTP method handler (i.e.on_get(req, resp, **kwargs)
,on_post(req, resp, **kwargs)
and so on). Due to this the only way to attachfalcon.before
hooks right now is through following boilerplate.So it is too verbose and also counterintuitive. Note that compatibility with falcon hooks is another feature we would like to have so we could support any falcon contrib package that provides hooks. This anyway should be discussed as a separate issue (see #31 )
Summary
Existing ways of handling custom context values are too verbose, require too much boilerplate and generally exploit
**kwargs
dictionary of list/retrieve/update/create methods that has completely different purpose in both falcon and graceful.I in my opinion the best approach would be to expose somehow the
Request.context
object in the basic resources. The best solution should:The text was updated successfully, but these errors were encountered: