json-rpc
json-rpc copied to clipboard
Way to pass additional data to dispatcher? [question/feature request]
I need to pass some additional data to the request dispatcher (a connection ID)
What is the best way to accomplish this? Currently I'm munging the request like so:
def munge_request(request, client_id):
try:
if isinstance(request, bytes):
decoded = request.decode("utf-8")
parsed = json.loads(decoded)
if 'params' in parsed:
parsed["params"] = {"orig_params": parsed["params"], "client_id": client_id}
return json.dumps(parsed)
except Exception as e:
log.error("Munge_request caught exception! %s" % e)
return request
This is obviously not optimal for multiple reasons. Is there a better way?
This issue is old but it's still open, I think I can contribute. Probably you want to modify the request object before it be dispatched to the procedure, it can be done with a middleware system. The dispatcher resolves the method call by retrieving the corresponding method implementation from the request's method name, passes the request's params through the middlewares which may transform it, then dispatches the call to the retrieved procedure. Most of web frameworks, for example, have a middleware system because it makes the framework much more flexible for extensions, you can take Django's middlewares as reference.
JSONRPCResponseManager
would need to be changed because the Dispatcher
is really a procedure mapper, who actually dispatches the calls is the JSONRPCResponseManager
.
Good idea!
On Mon, 7 Sep 2020, 23:05 Heraldo Lucena, [email protected] wrote:
This issue is old but it's still open, I think I can contribute. Probably you want to modify the request object before it be dispatched to the procedure, it can be done with a middleware system. The dispatcher resolves the method call by retrieving the corresponding method implementation from the request's method name, passes the request's params through the middlewares which may transform it, then dispatches the call to the retrieved procedure. Most of web frameworks, for example, have a middleware system because it makes the framework much more flexible for extensions, you can take Django's middlewares https://docs.djangoproject.com/en/dev/topics/http/middleware/ as reference.
JSONRPCResponseManager would need to be changed because the Dispatcher is really a procedure mapper, who actually dispatches the calls is the JSONRPCResponseManager.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pavlov99/json-rpc/issues/79#issuecomment-688523354, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEQDVNOCPEYVH2QKPJUPSLSEVKJFANCNFSM4EE7EKOQ .
@pavlov99 Thinking better, a more simple approach is to allow the response manager to accept a context
object, which could be used by users to pass additional information that did not come in the request's params. One example of use cases for that are HTTP cookies which generally store session's tokens that are needed to authenticate the request. In the ideal usage of RPC all needed data to process the request would be sent in the request's params, but generally HTTP is the transport used, json-rpc provides facilities to integrate itself to popular web frameworks.
I've implemented this feature in PR #122. With those changes, you can call dispatcher.update_context({'key': val}
to pass in context data. You will also need to add context_arg='{NAME_OF_FUNC_ARG}'
to your @dispatcher.add_method(...)
decorator.
NOTE: I needed access to the JSON-RPC message id
, but since JSONRPCResponseManager
loops over the JSON-RPC messages for us, I edited JSONRPCResponseManager
so it automatically calls dispatcher.update_context({"id": request._id})
for the user.
Folks, I was really slow in addressing this. Let me gather more information from the crowd.
What would be an example of context you'd like to use? I think any session/authentication related information are better to be passed via framework's request. There are examples for Django and Flask, e.g. https://github.com/pavlov99/json-rpc/blob/master/jsonrpc/backend/django.py#L60-L63 where request argument is injected to each method.
Adding this functionality to dispatcher sounds like an overhead to me. Dispatcher is basically a map of method name to method, having more responsibilities assigned to it could over-complicate the design. Generally, manager is a place to handle context. Using middleware could be an option.
This is honestly so old, I can't even remember what my use case was.
Just as @flound1129 said back on 2017-11-22, we need access to a WebSocket connection ID as well as the JSON-RPC id
. The reason we need it is that there's a resource that we need to access serially, so some of our JSON-RPC method handlers don't respond to the JSON-RPC request directly but instead queue it up to be handled by a different AWS Lambda.
Here's a flow diagram of the simple case, where we can respond in the same Lambda and do not need the JSON-RPC id
:
Here's a flow diagram of the complicated case, which needs the WebSocket connection ID and JSON-RPC id
:
@pavlov99, I agree that additional context should be passed in to JSONRPCResponseManager
and not be saved in Dispatcher
(especially when I realized that it's not thread-safe to store it in a global). Unfortunately, your Django example forces all handler functions to have a request
parameter, but we only want the functions that need the additional context to include it, on an opt-in basis. For readability, a user should be able to look at the decorator along with the function signature to know what JSON-RPC params are needed, and seeing context_arg='...'
in the decorator makes it clear that it's not a JSON-RPC param.
In its role as the method registry, Dispatcher
seems to be the correct place to register which functions need the additional context. I followed your suggestions and updated the Pull Request to move the context setting out of Dispatcher
and am now passing the whole request
object instead of just the request._id
.