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:
-
Add an
else
parameter to theroute
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 { ... }
-
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. -
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
-
How is HTTP data handled? (IE, interpret
Content-Type
s)- Form Data
- JSON: Would be nice to automatically convert to structs.
-
What about Query Params?
-
How are cookies handled?
Presumably you would set them via
Response
and get them via...? -
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. -
Autoreloading. Maybe use the unix-y reloading thing. Maybe not.
-
Plugins? Would be nice to easily extend routes.
-
Pre-post hooks/filters?
-
Caching?
-
Session support?
This is basically a server-side local store identified via an ID in a cookie.
-
Environment support? (debug vs. production vs. test, etc.)
-
Model validation?
-
Internationalization?
-
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?