Rust’s Iron Framework: Getting Started
The beginning: Rust’s Iron Framework: First impressions
I’m learning Iron and its ecosystem as I work through this application. It could be there are better ways of doing things using Iron I don’t know about yet.
I’ve started work on my time keeping application. I’m calling it Work, Life, Balance (WLB). I like to start new applications by building a user interface with the version 1.0.0 features backed by fake data. Working on the UI first gives me a feel for how I want to interact with the application and may point out flaws in my plans. I needed to figure out how to get a UI onto my monitor from Iron.
The main Iron crate doesn’t come with a templating language like mustache, handlebars, or twirl. Hand rolling HTML responses is not my idea of a good time so, right out of the gate, I had to determine what templating libraries are available in Rustland and which of them are reasonable to use with Iron. The answer is, there isn’t much. In fact, I only found one templating library that has been adapted specifically for Iron; handlebars-iron. The rest probably could be adapted but handlebars is a decent templating language and it’s ready for Iron.
Next, I had to get handlebars-iron working. Thankfully there’s a decent example in the README of the handlebars-iron source code repo. After a relatively short amount of time I wound up with my first Iron application.
// ./src/main.rs
extern crate env_logger;
extern crate iron;
use iron::prelude::{Chain, Iron, Request, Response};
use iron::{Set,status};
extern crate handlebars_iron as hbs;
use std::error::Error;fn main() {
env_logger::init().unwrap();let mut hbse = hbs::HandlebarsEngine::new();
hbse.add(Box::new(hbs::DirectorySource::new("./src/views/", ".hbs")));
if let Err(r) = hbse.reload() {
panic!("{:?}", r.description());
}
let mut chain = Chain::new(|_: &mut Request| {
let mut resp = Response::new();
resp.set_mut(hbs::Template::new("index", "".to_string())).set_mut(status::Ok);
Ok(resp)
});chain.link_after(hbse);
let port = 3000;
let bind_addr = format!("localhost:{}", port);
let _server_guard = Iron::new(chain).http(bind_addr.as_str()).unwrap();
let version = include_str!("version.txt");
println!("Running WLB v{} on port {}.", version, port)
}// ./src/views/index.hbs
<!DOCTYPE html>
<html lang="en">
<head>
<title>Work Life Balance</title>
</head>
<body>
Are you balanced?
</body>
</html>
Ref commit: https://github.com/ereichert/wlb/tree/9575971f99c9f927d2466fd09c0ef3a99addab22
There is a lot going on in this program. Here are a few highlights some of which I had to research.
I made up the folder structure based on other frameworks I’ve used. I have no idea how src/views is going to work out but it seems reasonable at the moment.
Producing HTML based on a template seems relatively straight forward as far as handlebars-iron is concerned. Hooking it up to Iron introduced some new concepts.
Chains
An Iron Chain (it’s always nice when a name works out) encapsulates a processing pipeline made up of Middleware components.
It implements the Handler trait so it can be used anywhere a Handler is required.
As the documentation states, the Chain processes a Request as follows.
- The Request goes through all BeforeMiddleware.
- Then a Handler implementation, provided to the Chain by us, must create a Response wrapped in an IronResult.
- Then all AfterMiddleware processes both the Request and the Response.
- And, finally, the Response is sent to the requester.
Interesting notes:
The Chain is a Handler that takes and Handler.
At any point in the processing pipeline, the Chain, an error can occur and the error is signaled by the Middleware in which the error occurred by passing on an IronError to the next Middleware in the Chain.
Any Middleware after our custom Handler passes on the original Request and A Response, not necessarily, THE Response.
The first version of WLB has two Handlers. The Handler I implemented and passed to the Chain and the Chain which is passed to the server. All Requests are going to be passed to the Chain for processing.
Now that I understand Chains it’s pretty easy for me to imagine the various Middleware and how they are used to augment Iron’s behavior.
The last note I have about the first version of WLB is about the following line of code.
let _server_guard = Iron::new(chain).http(bind_addr.as_str()).unwrap();
The _server_guard isn’t used for anything in the program and I originally wrote the following instead.
Iron::new(chain).http(bind_addr.as_str()).unwrap();
The problem with the second version is that the result of the http() call is the server itself. If you don’t bind it to an identifier it goes out of scope and the server dies. The example on the Github repo’s front page and the example in the docs are not correct.
Just a little gotcha.
I have a working application. A little less than a blank page. Something to work with. I’ll be working on the UI for the foreseeable future. That’s not Iron specific so, unless something interesting pops up, I won’t be posting until I get back to the Iron specific details of the project.