nickel-org / nickel.rs Goto Github PK
View Code? Open in Web Editor NEWAn expressjs inspired web framework for Rust
Home Page: http://nickel-org.github.io/
License: MIT License
An expressjs inspired web framework for Rust
Home Page: http://nickel-org.github.io/
License: MIT License
Following the demo steps on the nickel.rs website went well until I got this compiler error:
--> error: linking with cc
failed: exit code: 1
Once I installed libssl-dev, the demo compiled correctly and ran. You may want to consider adding this to the notes, or check for it in your code.
Was testing on Ubuntu 14.04 Desktop.
-Thanks!
-- Nick
This would be nice to setup, there's probably an option in cargo/rustdoc somewhere to support this.
We ignored proper error handling until know.
Here is a good overview about what's idiomatic Rust error handling by @thehydroimpulse: http://hydrocodedesign.com/2014/05/28/practicality-with-rust-error-handling/
I would love to have a logo inspired by this:
http://etc.usf.edu/clipart/20500/20594/cube_20594_lg.gif
That's because there you can have the bottom side as a real "floor" and maybe put something rust logo inspired on it?
@PascalPrecht @stefankroeckel are you interested to help with that?
Floor does currently not allow routes with variables such as /user/:userId
.
We can take some inspiration from rustful which already handles such routes.
My plan is to stick as closely to the expressjs API as possible. However, since JS is weakly typed and Rust is strongly typed we might have to make some trade offs.
In expressjs you would simply access your userId variable like this:
function(request, response) {
var userId = request.params.userId;
}
it should be fairly easy to come up with something somewhat similarish for us:
Solution 1:
fn sample_handler (request: &Request, response: &mut ResponseWriter) -> () {
let userId = request.params.get("userId");
};
ideally we would love to have this:
Solution 2:
fn sample_handler (request: &Request, response: &mut ResponseWriter) -> () {
let userId = request.params.userId
};
The latter might be even possible but I'm not as far down the road to answer it yet.
The middleware system express uses (and which is soon to land in Floor as well) allows a stack of function to run before each request. However, those run before any route handler and are great for pretty generic things like a body parser or logging.
However, wouldn't it be also nice to be able to register pre/post hooks per route?
Like this:
let some_handler = fn (req: Request, res: Response) { //do something; };
let pre_hook1 = fn (req: Request, res: Response) { //do something; };
let pre_hook2 = fn (req: Request, res: Response) { //do something; };
let post_hook1 = fn (req: Request, res: Response) { //do something; };
let post_hook2 = fn (req: Request, res: Response) { //do something; };
let server = Floor::new();
server
.get("/some/route", some_handler)
.pre_hook(preHook1)
.pre_hook(preHook2)
.post_hook(postHook1)
.post_hook(postHook2);
I wonder how @Kyjan @PascalPrecht and @apinnecke think about it
I believe that currently the only way to send a non-"text/plain" response is via reaching into response.origin
, for example w/ something like this.
response.origin.headers.content_type = Some(MediaType {
_type: "text".to_string(),
subtype: "html".to_string(),
parameters: Vec::new()
});
let _ = response.origin.write("<html></html>");
This is due to this in response.rs.
I don't have a particular approach in mind.
request.origin.request_uri
could be useful (with some configurable default, probably defaulting to "text/html").response.send_with_code_and_headers
could be useful.send
should take a trait implementation, allowing for an assortment of structs to be sent as responses.Hi, I'm new to Rust. I'm very excited to try this new web framework backed by this new awesome language.
I'm having problems compiling the project, when compiling rust-openssl specifically.
This is what the error looks like:
$ make all
cargo build -v
Running `rustc src/http/lib.rs --crate-name http --crate-type lib -g --out-dir /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps`
Running `rustc lib.rs --crate-name openssl --crate-type lib -g --out-dir /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps`
Running `rustc src/lib.rs --crate-name floor --crate-type lib -g --out-dir /Users/jcivancevich/Desktop/floor/target -L /Users/jcivancevich/Desktop/floor/target -L /Users/jcivancevich/Desktop/floor/target/deps`
Compiling openssl v0.0.0 (https://github.com/sfackler/rust-openssl.git)
Could not execute process `rustc lib.rs --crate-name openssl --crate-type lib -g --out-dir /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps -L /Users/jcivancevich/Desktop/floor/target/deps` (status=101)
--- stderr
error: multiple input filenames provided
Thanks for your great work!
Hopefully, I can contribute to this project soon :)
It would be neat if we could optionally pass a struct
to the handler function to have the request parameter / data automatically be deserialized into that struct. Just like ASP.NET MVC does it.
Something like this:
struct Person {
first_name: String,
last_name: String
}
fn user_handler (request: Request, response: &mut ResponseWriter, person:Person) {
};
Alternatively something like this may also be possible.
fn user_handler (request: Request, response: &mut ResponseWriter) {
let first_name = request.data.first_name;
};
server.get<Person>("/user/:userid", user_handler);
This may or may not be possible with Rust. Needs investigating. People on IRC also pointed out that macros could be helpful here but I'm not sold yet. Would rather like to solve this with Generics and without macros. I'm sure @Kyjan would also be interested in this kind of things :)
Tried to setup my nickel.rs site on áēww.bennyklotz.at:3000
Setup steps:
$ cargo build --release
$ ./target/release/website &
After starting my binary in background everything worked fine, after I closed the ssh connection to my server and exited the shell "No data received" was returned.
Here my coresponding src/main.rs file and screenshots without StaticFilesHandler:
extern crate http;
extern crate nickel;
use std::io::net::ip::Ipv4Addr;
use nickel::{
Nickel, Request, Response
};
fn page (_request: &Request, response: &mut Response) {
response.set_content_type("html");
response.send("<h1>Hello Rust!</h1>");
}
fn main() {
let mut server = Nickel::new();
// server.utilize(Nickel::static_files("assets/"));
server.get("/rust", page);
server.listen(Ipv4Addr(134, 119, 3, 19), 3000);
}
After exiting from the ssh shell it's still working:
http://s30.postimg.org/9g9k8qpkh/working_root.png
http://s27.postimg.org/9cxm4cp8z/working_rust.png
Here my coresponding src/main.rs and screenshots with StaticFilesHandler:
extern crate http;
extern crate nickel;
use std::io::net::ip::Ipv4Addr;
use nickel::{
Nickel, Request, Response
};
fn page (_request: &Request, response: &mut Response) {
response.set_content_type("html");
response.send("<h1>Hello Rust!</h1>");
}
fn main() {
let mut server = Nickel::new();
server.utilize(Nickel::static_files("assets/"));
server.get("/rust", page);
server.listen(Ipv4Addr(134, 119, 3, 19), 3000);
}
Before exiting fom the ssh connection everything works:
http://postimg.org/image/5v8l4o79r/
http://postimg.org/image/e20kw8xcf/
After exiting from the ssh connection it doesn't work anymore:
http://postimg.org/image/76cppvist/
http://postimg.org/image/jm9fjmc4t/
One of dependencies, https://github.com/chris-morgan/rust-http.git, has a notice in README.md, that library is obsolete.
Any plans to replace it? (with recommended teepee maybe)
express.js passes errors with it's next(err)
callback.
//cc @thiagopnts @janpantel
Consider this example:
extern crate http;
extern crate nickel;
use std::io::net::ip::Ipv4Addr;
use nickel::{
Nickel, Request, Response
};
fn main() {
let mut server = Nickel::new();
fn a_handler (_request: &Request, response: &mut Response) {
response.set_content_type("html");
response.send("hello world");
}
server.utilize(Nickel::static_files("assets/"));
server.get("/foo", a_handler);
server.listen(Ipv4Addr(127, 0, 0, 1), 6767);
}
In https://github.com/nickel-org/nickel.rs/blob/master/src/static_files_handler.rs#L28 FileNotFound error is thrown and therefore routes defined after the StaticFiles Middleware are not processed anymore.
In thinking of middlewares, shouldn't the StaticFiles Middleware pass this on to the remaining middleware stack if it isn't able to process it.
Cheers benny
Could not compile anymap
.
--- stderr
src/lib.rs:56:41: 56:44 error: explicit lifetime bound required
src/lib.rs:56 impl<'a> UncheckedAnyRefExt<'a> for &'a Any {
^~~
src/lib.rs:74:48: 74:51 error: explicit lifetime bound required
src/lib.rs:74 impl<'a> UncheckedAnyMutRefExt<'a> for &'a mut Any {
^~~
src/lib.rs:111:31: 111:34 error: explicit lifetime bound required
src/lib.rs:111 data: HashMap<TypeId, Box, TypeIdHasher>,
^~~
error: aborting due to 3 previous errors
[root@localhost nickel]# rustc -v
rustc 0.12.0-pre-nightly (5419b2ca2 2014-08-29 22:16:20 +0000)
When make
is invoked with -j2
, deps
and floor
are being built simultaneously, which ends up with:
src/lib.rs:18:1: 18:19 error: can't find crate for `http`
Additionaly, make
in subdirectories should not be invoked directly, but rather as $(MAKE)
. And also we can avoid rebuilding everything with each invocation of make
.
But this failing with -j2
problem is the most important one, because I think it's really common case for people to have such flags as defaults.
The nickel homepage has a dead link
http://docs.nickel.rs/site/
Here is the dead link
http://docs.nickel.rs/getting-started.html
I think that the dead link should be replaced with
http://nickel.rs/getting-started.html
BTW: The "getting started guide" looks nice and useful.
Ok(Halt)
, Ok(Continue)
, Err(err)
I would like to define my handlers as a closure instead of a named function. For example:
router.get("/some/*/route", |_request: &Request, response: &mut Response| {
response.send("foo");
});
Is there a reason .utilize()
is used instead of .use()
? 'Utilize' is a bit of an ugly filler word, and it's (perhaps) needlessly different from Express and other frameworks.
Would be great to have a real project page (also wrapping the documentation) that is itself served by Floor and hosted on heroku.
How about spawning a task for each request? Rust has awesome Tasks built in. Otherwise 1 long request will cause freezing whole app.
The goal of this project is to create a web framework in Rust with a focus on simplicity. It should be easy to learn. Therefore it takes much inspiration from express.js. I wonder if it would help community adoption to actually rename it to express.rs.
Take a look at this for a reference
Maybe something inspired by the metal. Could also be inspired by the coin but I think I like the shape of a metal more.
Some inspiration:
https://www.google.de/search?q=nickel&safe=off&es_sm=91&source=lnms&tbm=isch&sa=X&ei=NQ7bU6WAA_Ha0QXprIGgDg&ved=0CAgQ_AUoAQ&biw=1440&bih=722
I think we recently screwed this up. Should be easy to have tests for that.
E.g. mustache.rs
Just like we did with the macro.
E.g.
/user/*/:userid
should match:
/user/foo/4711
/user/bar/4711
We should wrap the ResponseWriter
with our own Response
struct and then move stuff life header handling there.
This is exactly what expressjs does:
https://github.com/visionmedia/express/blob/9bf1247716c1f43e2c31c96fc965387abfeae531/lib/response.js
We currently hard code the header which obviously is the wrong thing: https://github.com/cburgdorf/Floor/blob/master/src/server.rs#L28
If we would have methods like send
, json
etc on our Response
object, we could figure out the correct header automatically (but still allow overwriting if needed).
This is exactly how express does it I think.
I think we are using way too many String
allocations than needed. Here is a great explanation of the difference between &str
and String
:
http://stackoverflow.com/questions/24158114/rust-string-versus-str/24159933#24159933
We should check if we can improve the code base with fewer usage of String
It would improve readability to change following:
let text = String::new()
.append("This is user: ")
.append(request.params.get(&"userid".to_string()).as_slice());
response.write(text.as_bytes());
into following:
write!(response, "This is user: {}", request.params.get(&"userid".to_string()));
In a language that has great enum support using a boolean value feels a bit hackish. It would be better to create an enum with meaningful names such as Continue
and Halt
.
I was just pointed to a bug by @bguiz that roughly goes like this:
If you use the static_files
middleware then all your responses from your regular matched route handler will be prepended with Not Found. It's unfortunately only the tip of the iceberg pointing out a bigger design flaw with the current request pipeline that nickel uses.
Why does that happen?
This is how the request pipeline in nickel looks like:
Request -> MiddlewareHandler -> MatchedRouteHandler
|
> ErrorHandler
What this means is that when your /foo/bar
handler runs, the static_files
handler has already yield an error because it couldn't find a file to serve at /foo/bar
.
Why did this work before we had error handler?
Because at that time the static_files
handler just set the status to NotFound
and continued. Then your /foo/bar
handler set the status to Ok
again and everything lived in peace and harmony. Unfortunately though, there was no way to do things like custom error handler.
What do we want?
We want a request pipeline that looks more like this
Request -> MW:json_body_parser -> MW:foo -> MatchedRouteHandler -> MW:static_files
| | | |
> ErrorHandler > EH > EH > EH
This would make sure that the static_files
middleware only runs if no other handler cut off the pipeline before.
I think the easiest solution to achieve this would be to make the router just a regular middleware that one can use with server.utilize(Nickel::router())
. In order to get the pipeline from the picture above one could then do the boostrapping like this:
server.utilize(Nickel::json_body_parser())
server.utilize(Nickel::router())
server.utilize(Nickel::static_files("assets/"));
I think this is roughly what express does as well. They somehow also let you get away without adding a router explicitly but I didn't look into such details yet. I think we might be able to introduce that first and add some sugar later.
Thoughts? @bguiz @nielsle @janpantel
Changelog generation should be done with https://github.com/thoughtram/clog. I guess we just need a bit of scripting to increase version number, invoke clog and trigger a commit.
Would be great to use that as a real life example to actually test out things like the static file handler. I guess it's plenty of bugs but nobody noticed until now :)
e.g.
let mut server = Nickel::new();
server.post("/foo/:bar", foo_handler);
will return 404 when requesting
http://localhost:3000/foo/bar.baz
Would you be open to have Response optionally send a status code in addition to the body text? That seems to be typical for many web frameworks in other languages.
This is what expressjs does behind the covers:
https://github.com/visionmedia/send/blob/master/index.js
Reading a file and returning it's contents should also be fairly straight forward in Rust
https://github.com/jroweboy/oxidize/blob/master/examples/hello_world/src/hello.rs#L79
But we need to handle all the different edge cases.
I already tried it but this broke our documentation so I quickly rolled back. We need to make sure to change our documentation upgrade script to push into right repository before we move. That's because organization pages work slightly different from private repository pages.
The following code builds today:
extern crate nickel_postgres;
extern crate nickel;
use std::io::net::ip::Ipv4Addr;
use self::nickel::{Nickel, Request, Response, JsonBody};
use nickel_postgres::{ PostgresMiddleware, PostgresRequestExtensions };
fn main() {
let mut server = Nickel::new();
fn db_handler(req: &Request, response: &mut Response)
{
let db_conn = req.db_conn();
response.send("doesn't work");
}
let mut router = Nickel::router();
router.get("/", db_handler);
server.utilize(router);
server.listen(Ipv4Addr(127, 0, 0, 1), 6767);
}
db_handler()
will never be able to retrieve a database connection since we haven't utilized a PostgresMiddleware and we will get a crash at every request.
In my opinion, the whole point of writing web applications in a statically typed language is that the compiler will catch this kind of bugs for us. With the current situation we're no better than Python/Ruby/Whatever in that regard.
Could we discuss some approaches to solving this? I have some ideas that I'm going to try out but I'd like to hear some more thoughts on this too.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
đ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. đđđ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google â¤ī¸ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.