How to get the url parameters within `actix-web`?

By using Python package Flask, I can get the url parameters like this:

class Dash:
    def __init__(self):
        self.server = flask.Flask("main")
        self.setup_routes()

    def setup_routes(self):
        url = "/_dash-component-suites/<string:package_name>/<path:fingerprinted_path>"
        self.server.add_url_rule(url, view_func=self.serve_component_suites, methods = ["GET", ])

    def serve_component_suites(self, package_name, fingerprinted_path):
        data = open(f"./comp/{fingerprinted_path}", "rb").read()
        response = flask.Response(data, mimetype="application/javascript;charset=utf-8")
        print(response.headers)
        return response

The url lies in the method setup_routes, it has two paramters package_name and fingerprinted_path.

Then I try to do the same thing in Rust crate actix-web, like this:

#[get("/_dash-component-suites/{package_name}/{fingerprinted_path}")]
async fn serve_component_suites(path: Path<(String, String)>) -> impl Responder {
    let (package_name, fingerprinted_path) = path.into_inner();
    println!("{:?}", package_name);
    let mut f = std::fs::File::open(format!("./comp/{}", fingerprinted_path)).unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer).unwrap();
    HttpResponse::Ok()
        .content_type("application/javascript;charset=utf-8")
        .body(buffer)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
        App::new()
            .service(serve_component_suites)
    })
        .workers(10)
        .bind(("127.0.0.1", 8081))?
        .run()
        .await
}

The same url lies above the function serve_component_suites. But it can't work. What am I doing wrong?

To me it looks like you are using the Path extractor the right way. What exact behaviour is your program exhibiting when you make a request to that endpoint? What are your package_name and fingerprinted_path?

This link is the context of Python code, and this link is the Rust code.
The Python code work well, but the Rust code seems failed to run the function serve_component_suites. When I run the Rust code, the web has the following error:


The function can't get the files /_dash-component-suites/dash/dcc/async-graph.js and /_dash-component-suites/plotly/package_data/plotly.min.js.
And when I run the Python cdoe, the terminal has this error:

127.0.0.1 - - [09/Oct/2023 23:33:22] "GET /_dash-component-suites/dash/dcc/async-graph.js HTTP/1.1" 200 -
[2023-10-09 23:33:22,743] ERROR in app: Exception on /_dash-component-suites/dash/dcc/async-graph.js.map [GET]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2529, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "dash.py", line 24, in serve_component_suites
    data = open(f"./comp/{fingerprinted_path}", "rb").read()
FileNotFoundError: [Errno 2] No such file or directory: './comp/dcc/async-graph.js.map'

but the web page seem work fine, and the everything works fine too:

In the pthon code, after I comment the line: self.server.add_url_rule(url, view_func=self.serve_component_suites, methods = ["GET", ]) (lies in the method setup_rounts), then the Python code has the error the same of previous Rust code. So I guess, the Rust code didn't run the function serve_component_suites successfully.

The package_name and fingerprinted_path are the routes generated by the javascript from the website. The website generated some files like this:

Could it be that /_dash-component-suites/dash/dcc/async-graph.js hash too many slashes (components)? It would be weird if / could be part of a segment. That would explain why the rust code in the handler is not run at all.

2 Likes

That is exactly the reason. I change the function server_component_suites to this:

#[get("/_dash-component-suites/{path1}/{path2}/{path3}")]
async fn serve_component_suites(path: Path<(String, String, String)>) -> impl Responder {
    let (_path1, path2, path3) = path.into_inner();
    let mut f = std::fs::File::open(format!("./comp/{path2}/{path3}")).unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer).unwrap();
    HttpResponse::Ok()
        .content_type("application/javascript;charset=utf-8")
        .body(buffer)
}

Then, everything works fine. Unlike python package flask, the macro #[get] in actix_web can't have a Path extractor like path1/path2 in one parameter.
Thanks very much.

It seems also possible to match more than one, look here under "tail match": URL Dispatch | Actix

1 Like

Yes, I write the function in this way, it works fine too:

#[get("/_dash-component-suites/{path1}/{path2:.*}")]
async fn serve_component_suites(path: Path<(String, String)>) -> impl Responder {
    let (_path1, path2) = path.into_inner();
    let mut f = std::fs::File::open(format!("./comp/{path2}")).unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer).unwrap();
    HttpResponse::Ok()
        .content_type("application/javascript;charset=utf-8")
        .body(buffer)
}

The difference between python code <path1>/<path2> and rust code {path1}/{path2:.*}, I thought it may comes from the thought: when the url is like p1/p2/p3, path1 can be p1 or can be p1/p2.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.