Announcing Gotham 0.4
It’s been a little while, but today we’re happy to announce the latest release of the Gotham web framework: Gotham 0.4! This release focuses on some of the more common use cases when writing web services, by introducing several new middlewares and features to improve productivity when working with Gotham.
TLS Support
One of the larger features included in this release is the support for TLS when working with Gotham. This is handled via the popular Rust TLS library rustls, in combination with the Tokio bindings provided by tokio-rustls.
Creation of a TLS server is consistent with the existing Gotham API, with
the only difference being the requirement of a rustls::ServerConfig
to
be provided at server startup:
let addr = "127.0.0.1:7878";
let config = rustls::ServerConfig::new(...);
gotham::tls::start(addr, || Ok(say_hello), config)
This simple tweak to the API makes it easy to handle TLS connections without having to learn an entire new set of APIs.
Middlewares
Gotham 0.4 includes work on a few new middlewares designed to improve some common use cases, and fit in with common patterns when writing web services. Each of the middlewares below is available in either the core of Gotham itself, or distributed as a separate crate on crates.io.
Cookie Middleware
Gotham 0.4 also includes a new middleware named CookieParser
, which is
designed to make dealing with cookies much simpler and in a much more
automatic fashion. All parsing of cookies is done via the de-facto
cookie crate.
Unlike some of the other middlewares, the CookieParser
middleware ships
directly in Gotham’s core library due to it being re-used in the existing
SessionMiddleware
workflows, and as it’s such a common requirement when
building a web service.
To use this middleware, configure it as any other middleware inside your router:
// create a middleware pipeline from our middleware
let pipeline = single_middleware(CookieParser);
// construct a basic chain from our pipeline
let (chain, pipelines) = single_pipeline(pipeline);
// build a router with the chain & pipeline
gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| {
route.get("/").to(say_hello);
}))
Then you will automatically have a CookieJar
available on the State
provided to your request, which you can easily access as needed:
CookieJar::borrow_from(&state)
If you have any existing middlewares dealing with cookies, maybe for
things like authorization, you can now place them after the CookieParser
in the middleware chain and they will then also have access to the new
CookieJar
entity. This allows you to remove any custom cookie parsing
that you may have had to write in the past. One example of this is the
existing SessionMiddleware
, which will now look for any previously
parsed cookies t avoid doing unnecessary work.
An example of this middleware is available in the repository, so please do check it out.
Diesel Middleware
Diesel is a very popular ORM
written in Rust, and there has been some work in progress for a compatible
middleware for some time. Thanks to the work of @colinbankier
this
is now available via the gotham_middleware_diesel
crate, which offers
a convenient API for interacting with Diesel from Gotham.
This middleware introduces a Repo
struct, which is used as a layer
between Diesel and Gotham to ensure that database interaction can be
easily chained alongside other asynchronous operations. This structure
is fairly straightfoward and offers an easy way to interact with Diesel
from inside Gotham:
// create a new repo, in this case just using a SQLite setup
let repo: Repo<SqliteConnection> = Repo::new("products.db");
// create a middleware pipeline from our middleware
let pipeline = single_middleware(DieselMiddleware::new(repo));
// construct a basic chain from our pipeline
let (chain, pipelines) = single_pipeline(pipeline);
// build a router with the chain & pipeline
gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| {
route.get("/").to(say_hello);
}))
From there you gain simple access to Repo
on the request state, just
like when using other middlewares. You can then use the Repo
to execute
database calls on a separate connection pool:
// borrow the repo from the state
let repo = Repo::borrow_from(&state);
// execute database calls
repo.run(move |conn| {
diesel::insert_into(products::table)
.values(&product)
.execute(&conn)
})
What may not be obvious from the snippet above is that calling repo.run
actually returns a Future
, allowing you to seamlessly sprinkle your
database calls amongst other asynchronous handler code without going
through too much pain.
This is the main advantage of the Repo
type as it manages the
synchronous calls of the underlying connections on a separate thread
pool, and maintains the appearance that you’re working with an API
that’s fully asynchronous to keep things simple.
Naturally a complete example of database interaction is a little too long to embed into a blog post, so please check out the excellent examples Colin provided in the main repository.
JWT Middleware
JSON Web Tokens (usually referred to as JWT) have become a popular way to authenticate against HTTP APIs due to their ability to pass around data in a secure manner, and in an efficient way. In Gotham 0.4, we’re excited to include a simple middleware to validate these tokens, so that you don’t have to!
As a user of this middleware, you are able to deserialize your web
tokens directly into a custom structure automatically. All required
validation is taken care of by the middleware itself by checking
tokens provided in the HTTP Authorization
header.
In the example below we’re defining a new Claims
structure to
represent our tokens. This structure is automatically populated
by the middleware, and is available in your API handlers.
#[derive(Deserialize, Debug)]
struct Claims {
sub: String,
exp: usize
}
// create a middleware pipeline from our middleware
let middleware = JWTMiddleware::<Claims>::new("secret".as_ref());
let pipeline = single_middleware(middleware);
// construct a basic chain from our pipeline
let (chain, pipelines) = single_pipeline(pipeline);
// build a router with the chain & pipeline
gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| {
route.get("/").to(say_hello);
}))
Then, using the typical state access that Gotham provides, you are
able to fetch your Claims
structure back out from inside your API
code:
// borrow the claims from the state
let claims = Claims::borrow_from(&state);
As this is the first implementation of this middleware, please let us know if you have any suggestions or ways we can extend these use cases!
Miscellaneous
Although not technically part of the Gotham core release, we have also added several new examples to the repository. Thank you to all who had any input in the creation of:
In addition to everything mentioned, we have also updated dependencies to their latest versions as well as migrated the codebase to compile under Rust 2018 edition to make contribution a little easier. So please do get involved if there’s something you’d like to see included!