Rocket/docs/idea.md

5.1 KiB

Rust Web Framework

I really want a nice, easy to use, safe, and stupid fast web framework for Rust. I don't want a monolithic Rails like thing. I'd much rather have something that looks like Bottle. That is, use decorators to declare routes.

Nickel.rs looks kinda nice though (see example 2 below - that looks like, but isn't Nickel).

Here's what the simplest program might look like with this framework:

#[route("/")]
fn home() -> Response {
  Response::string("Hello, world!")
}

fn main() {
  RustWebFramework::run("localhost");
}

Alternatively...

fn home() -> Response {
  Response::string("Hello, world!")
}

fn main() {
  route! {
    get '/' => home,
  }

  RustWebFramework::run("localhost");
}

Arguments

Here's what a route that takes arguments might look like:

#[route("/<page>")]
fn home(page: &str) -> Response {
  Response::string(page)
}

The really neat thing here is that the route macro will typecheck the function signature. The signature should also have a return type of Response (or whatever the response type ends up being) and take a number of equivalent to those present in the route. The type of the arguments can be any T that implements From<&str>. The conversion will be done automatically by the route handler. As such, the following will work as expected:

#[route("/users/<id>")]
fn home(id: isize) -> Response {
  let response_string = format!("User ID: {}", id);
  Response::string(response_string)
}

If the conversion fails, the router should 1) print out a debug error message and return some user-set no-route-exists things, and 2) allow the programmer to catch the failure if needed. I'm not quite sure what the best way to allow 2) is at the moment. Here are a couple of ideas:

  1. Add an else parameter to the route macro that will take in the name of a function to call with the raw string (and more) if the routing fails:

    #[route("/users/<id>", else = home_failed)]
    fn home(id: isize) -> Response { ... }
    fn home_failed(route: &str) -> Response { ... }
    
  2. Allow the parameter type to be Result<T>. Then the route is always called and the user has to check if the conversion was successful or not.

  3. Pass it off as an error type to another handler.

Open questions here:

1.  What syntax should be used to match a path component to a regular
    expression? If for some parameter, call it `<name>`, of type `&str`, we
    want to constrain matches to the route to `name`s that match some
    regular expression, say `[a-z]+`, how do we specify that? Bottle does:

        <name:re:[a-z]+>

    We can probably just do:

        <name: [a-z]+>

Methods

A different HTTP method can be specified with the method route argument:

#[route(method = POST, "/users")]
fn add_user(name: &str, age: isize) -> Response { ... }

Or, more succinctly:

#[POST("/users")]
fn add_user(name: &str, age: isize) -> Response { ... }

Route Priority

Do we allow two routes to possibly match a single path? Can we even determine that no two paths conflict given regular expressions? Answer: Yes (http://arstechnica.com/civis/viewtopic.php?f=20&t=472178). And if so, which route gets priority? An idea is to add a priority parameter to the route macro:

For example:

#[GET("/[name: [a-zA-Z]+", priority = 0)]
#[GET("/[name: [a-z]+", priority = 1)]

The first route allows lower and uppercase letter, while the second route only allows lowercase letters. In the case that the entire route has lowercase letters, the route with the higher priority (1, here) gets called, i.e., the second one.

Error Pages

There's a route for error pages, too:

#[route(method = ERROR, status = 404)]
fn page_not_found(...not sure what goes here yet...) -> Response { .. }

Or, more succinctly:

#[error(404)]
fn page_not_found(...not sure what goes here yet...) -> Response { .. }

Open Questions

  1. How is HTTP data handled? (IE, interpret Content-Types)

    • Form Data
    • JSON: Would be nice to automatically convert to structs.
  2. What about Query Params?

  3. How are cookies handled?

    Presumably you would set them via Response and get them via...?

  4. Easy support for (but don't bundle it in...) templating would be nice. Bottle lets you do:

    #[view("template_name")]
    fn hello(...) -> HashMap { .. }
    

    and automatically instantiates the template template_name with the parameters from the HashMap.

  5. Autoreloading. Maybe use the unix-y reloading thing. Maybe not.

  6. Plugins? Would be nice to easily extend routes.

  7. Pre-post hooks/filters?

  8. Caching?

  9. Session support?

    This is basically a server-side local store identified via an ID in a cookie.

    http://entrproject.org/

  10. Environment support? (debug vs. production vs. test, etc.)

  11. Model validation?

  12. Internationalization?

  13. Be faster than https://github.com/julienschmidt/httprouter.

For 2, 3: the obvious solution is to have a Request object with that information. Do we need that, though? Is there something better?