My Web Framework. (Great name, I know)
This project was inspired by Gary Bernhardt's screencast on Destroy All Software about making a webframework from scratch.
This is not intended to be used in a production or production-like setting, so please don't even try to do so.
This is a backend web framework, like ASP.NET. The most important part of this is the router and route resolvers, which take in a route that has been requested from the server and try to find the handler which should accept it.
Examples can be found in the examples directory.
Here is a simple "Hello World" server:
struct HelloWorld;
impl RequestHandler for HelloWorld
{
fn handle(&self, _route_map: RouteMap) -> mwf::Result<View>
{
Ok(View::raw("Hello world!"))
}
}
fn main()
{
ServerBuilder::new()
.bind("/", HelloWorld)
.start();
}
The Resolver
trait, whose definition (as well as related one) is listed below,
will simply take in a URL which has been split up along the slashes, and return
None
if the path isn't accepted by the resolver, or Some(RouteMap)
if the
route was accepted.
pub type RouteMap = HashMap<String, String>;
pub struct ResolveParams<'a>
{
pub method: Method,
pub route: Vec<&'a str>,
}
pub trait Resolver
where Self: Send + Sync
{
fn resolve(&self, params: &ResolveParams) -> Option<RouteMap>;
}
The standard resolver (which is the default one enabled) has three path tokens:
- a literal token (
/foo
)- This matches text exactly as it appears in a URL
- a variable token (
/:foo
)- This matches any text in the same position, and the text it matched will
be stored in the
RouteMap
under the variable's name - Variables must begin with a leading
:
which is used in its name in theRouteMap
.
- This matches any text in the same position, and the text it matched will
be stored in the
- an optional variable token (
/:foo?
)- This matches anything in the same position (including nothing).
- The matched text will be stored in the
RouteMap
under the variable's name (""
if it matched nothing) - Like variables, optional variables must begin with a
:
and must also end with a?
. Both of these characters are part of the variable's name in theRouteMap
.
Using these tokens, you can build route handlers for a lot of things.
On the other side of request resolution, is the actual handler itself. Handler
is also a trait (shown below), but a much simpler one. This receives the
RouteMap
generated by its corresponding Resolver
and then serves a View
of the content which is meant to be at the URL. Because generating the page
might cause an error (who hasn't seen a 500 Internal Service Error
before?),
we don't want the entire server to crash on such a problem. For this reason, the
handler must actually return a mwf::Result
.
pub trait RequestHandler
where Self: Send + Sync
{
fn handle(&self, route_map: RouteMap) -> mwf::Result<View>;
}
Using the ServerBuilder
interface, handlers are attached to the server by
using the ServerBuilder.bind(method, route_spec, handler)
method, as shown
above in the hello world example.