json-rpc icon indicating copy to clipboard operation
json-rpc copied to clipboard

Way to pass additional data to dispatcher? [question/feature request]

Open flound1129 opened this issue 7 years ago • 7 comments

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?

flound1129 avatar Nov 22 '17 17:11 flound1129

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.

HMaker avatar Sep 07 '20 22:09 HMaker

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 avatar Sep 07 '20 23:09 pavlov99

@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.

HMaker avatar Sep 12 '20 17:09 HMaker

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.

ppena-LiveData avatar Jul 20 '21 15:07 ppena-LiveData

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.

pavlov99 avatar Jul 21 '21 21:07 pavlov99

This is honestly so old, I can't even remember what my use case was.

flound1129 avatar Jul 21 '21 21:07 flound1129

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: image

Here's a flow diagram of the complicated case, which needs the WebSocket connection ID and JSON-RPC id: image

@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.

ppena-LiveData avatar Jul 22 '21 18:07 ppena-LiveData