Routing Beta: Support for request.form_params and request.body_json

What I’m trying to do:
@stucork I want to say that the new Routing library is very cool! I noticed that right now it only supports request.query_params. The Http endpoints that I have need to support POST params and JSON body data. In my code, I use anvil.server.request.form_params or anvil.server.request.body_data to achieve this.

What I’ve tried and what’s not working:
I tried overloading “load_data()” in the Request class, but calling form_params or body_data isn’t showing much. For example:

Code Sample:

from routing.router import Route
import anvil.server

class Home(Route):
    path = "/"
    form = "Pages.Home"
      
    def before_load(self, **params):
      if anvil.is_server_side():
       p = anvil.server.requests.form_params
       print(f'BEFORE LOAD FORM PARAMS = {p}')
      return

    def load_data(self, **loader_args):
      params = loader_args
      print(f"Home Load Data: {params}")
      if anvil.is_server_side():
        p = anvil.server.requests.form_params
        print(f'LOAD FORM Form Params = {p}')
      return params

Possible solution:
It would be nice if you can pass on the original anvil.server.request information in the Request Context or as a parameter to the before_load and load_data hooks.

For now, I guess I can use the existing anvil.server.route decorator and redirect to a form.

Clone link:
share a copy of your app

@stucork

I just tested and realized that @anvil.server.route decorator will pass on either query or form [POST] parameters to the decorated function.

So, one simple fix in the Routing code: routing/client_code/router/_route.py, in the ‘create_server_route->route_handler’ function, line #44:

Instead of this:
search = encode_query_params(request.query_params)

Do this?:
search = encode_query_params(kwargs)

@anvil.server.route will pass on query or POST parameters to kwargs…I think.

Could you provide some more context about what you’re trying to do?
I don’t quite follow the use case from the clone link or description.

At the moment routes when navigated through client side code don’t go to the server,
which I think is what you’re trying to do.

I’ve thought about using server only routes that would force the router to send the request to the server.
But I’ve been waiting for a use case to think about it’s implementation.

(A problem with just using the kwargs, is that kwargs also includes path parameters.)

Hey @stucork!

We are using Autodesk Shotgrid (well they changed the name to Autodesk Flow recently)…it’s a web based project management application/database. It’s used to track assets, tasks…everything for special effects for film and tv. We work on shows that have hundreds of shots and Shotgrid helps us track our progress.

You can create custom menus in Shotgrid which are called AMI’s (Action Menu Items). You can right click on a row of shots and activate a custom menu (that I created) that will open up a modal dialog which is really an iframe to a custom webpage.

I am using Anvil to create the custom AMI dialog. Shotgrid calls the url to my Anvil http endpoint via POST instead of a GET. So all the info I need for the selected shots appear as form parameters instead of url parameters (via ‘?’).

Right now it all works with @anvil.server.route… something like this (simplification):

@anvil.server.route('/my_endpoint')
def my_endpoint(**param):
    datadict = {
        'shot_id': param.get('id'),
        'shot_name': param.get('shotname'),
        'project_name': param.get('sg_project_folder'),
        'project_long_name': param.get('project_name'),
        'project_id': param.get('project_id'),
        'episodes': [],
        'episode_name': param.get('episode')
        'app': 'ShotGrid',
        'sg_session_id': param.get('session_uuid'),
    }
    return anvil.server.FormResponse('forms.myAMI', datadict=datadict)

(I’m actually doing a lot more prepping of the parameters before I hand it to the form)

The short of it is, I don’t have control over how my Anvil endpoint is being called. I totally get that the Routing library is mainly used for client side navigation…but it would be nice to have access to the anvil.request object in something like “before_load” and “load_data” so we can pass on POST parameters and /or JSON body data.

Thanks for the quick response. I really appreciate it!

P.S. The recent update to Anvil.uplink which added anvil.server.route to uplink was a much needed upgrade for my workflow. Thanks!

Yes that makes sense. Thanks for the context.

You want a way to keep your existing anvil.server.route logic but co-locate this with the api provided by the routing dependency.

Feel free to turn this into a feature request at the github page.

As a way to achieve this currently you could probably do something like


class ShotGridRoute(Route):
    path = "/my_endpoint"
    def before_load(self, **loader_args):
        assert anvil.is_server_side(), "This route should only be called on the server by a shotgrid form"
        form_params = anvil.server.request.form_params
        raise Redirect("/some_path", query=form_params)


1 Like

Oh i’m an idiot…

I had anvil.server.requests with an “s” instead of anvil.server.request

It’s working now. Thanks for your help!

EDIT for posterity… Here’s code that works for me:

class Home(Route):
  path = "/home"
  form = "Pages.Home"

  def load_data(self, **loader_args):
    print("Home Load Data Called")
    if anvil.is_server_side():
      form_params = anvil.server.request.form_params
      print(f'LOAD FORM Form Params = {form_params}')
      return form_params
    else:
      return None
```
2 Likes