### Gotham Hello World Example Source: https://gotham.rs/ A basic "Hello World" application using Gotham. It defines a handler function and starts a server to respond to requests. ```rust //! A Hello World example application for working with Gotham. use gotham::state::State; const HELLO_WORLD: &str = "Hello World!"; /// Create a `Handler` which is invoked when responding to a `Request`. /// /// How does a function become a `Handler`?. /// We've simply implemented the `Handler` trait, for functions that match the signature used here, /// within Gotham itself. pub fn say_hello(state: State) -> (State, &'static str) { (state, HELLO_WORLD) } /// Start a server and call the `Handler` we've defined above for each `Request` we receive. pub fn main() { let addr = "127.0.0.1:7878"; println!("Listening for requests at http://{}", addr); gotham::start(addr, || Ok(say_hello)) } #[cfg(test)] mod tests { use super::*; use gotham::test::TestServer; #[test] fn receive_hello_world_response() { let test_server = TestServer::new(|| Ok(say_hello)).unwrap(); let response = test_server .client() .get("http://localhost") .perform() .unwrap(); assert_eq!(response.status(), StatusCode::Ok); let body = response.read_body().unwrap(); assert_eq!(&body[..], b"Hello World!"); } } ``` -------------------------------- ### Complete Static Site Serving Example Source: https://gotham.rs/blog/release/2018/10/29/gotham-0.3.html A full example of serving all assets from the 'assets' directory using Gotham. This is useful for static websites. ```rust extern crate gotham; use gotham::router::builder::*; pub fn main() { gotham::start("127.0.0.1:7878", build_simple_router(|route| { route.get("/*").to_dir("assets"); })) } ``` -------------------------------- ### Start Gotham Server Source: https://gotham.rs/ Start the Gotham server, providing the address and the configured router. ```rust /// Start a server and use a `Router` to dispatch requests pub fn main() { let addr = "127.0.0.1:7878"; println!("Listening for requests at http://{{}}", addr); gotham::start(addr, router()) } ``` -------------------------------- ### Start Gotham Server Source: https://gotham.rs/ Starts a Gotham server listening on the specified address with the provided router. ```rust /// Start a server and use a `Router` to dispatch requests pub fn main() { let addr = "127.0.0.1:7878"; println!("Listening for requests at http://{}", addr); gotham::start(addr, router()) } ``` -------------------------------- ### Rust: Complete Static Site Server Source: https://gotham.rs/blog A complete example of a Gotham application serving a static site from the 'assets' directory. ```rust extern crate gotham; use gotham::router::builder::* pub fn main() { gotham::start("127.0.0.1:7878", build_simple_router(|route| { route.get("/*").to_dir("assets"); })) } ``` -------------------------------- ### Start TLS Server with Gotham Source: https://gotham.rs/blog/release/2019/07/14/gotham-0.4.html Use this to start a TLS-enabled Gotham server. Requires a rustls::ServerConfig. ```rust let addr = "127.0.0.1:7878"; let config = rustls::ServerConfig::new(...); gotham::tls::start(addr, || Ok(say_hello), config) ``` -------------------------------- ### GET /products Source: https://gotham.rs/ Handles GET requests to the /products endpoint. It extracts a 'name' query parameter and returns a JSON response containing a Product object. ```APIDOC ## GET /products ### Description Handles GET requests to the /products endpoint. It extracts a 'name' query parameter and returns a JSON response containing a Product object. ### Method GET ### Endpoint /products ### Parameters #### Query Parameters - **name** (string) - Required - The name to be included in the product. ### Request Example ```json { "name": "example_name" } ``` ### Response #### Success Response (200) - **name** (string) - The name of the product. #### Response Example ```json { "name": "example_name" } ``` ``` -------------------------------- ### Gotham Router Example with Multiple Handlers Source: https://gotham.rs/ Demonstrates how to use Gotham's `Router` to associate multiple handlers with different HTTP verbs and paths, including nested scopes and associations. ```rust //! An example of the Gotham web framework `Router` that shows how to associate multiple handlers //! to a single path. use gotham::router::Router; use gotham::router::builder::*; use hyper::{Get, Head}; mod handlers; use self::handlers::*; /// Create a `Router` /// /// Results in a tree of routes that that looks like: /// /// / --> GET, HEAD /// | products --> GET, HEAD /// | bag --> GET /// | checkout /// | start --> GET /// | address --> POST, PUT, PATCH, DELETE /// | payment_details --> POST, PUT /// | complete --> POST /// | api /// | products --> GET /// /// If no match for a request is found a 404 will be returned. Both the HTTP verb and the request /// path are considered when determining if the request matches a defined route. /// /// The API documentation for `DrawRoutes` describes all the HTTP verbs which Gotham is capable of /// matching on. fn router() -> Router { build_simple_router(|route| { route.request(vec![Get, Head], "/").to(index); route.get_or_head("/products").to(products::index); route.get("/bag").to(bag::index); route.scope("/checkout", |route| { route.get("/start").to(checkout::start); // Associations allow a single path to be matched for multiple HTTP verbs // with each delegating to a unique handler or the same handler, as shown here with // put and patch. route.associate("/address", |assoc| { assoc.post().to(checkout::address::create); assoc.put().to(checkout::address::update); assoc.patch().to(checkout::address::update); assoc.delete().to(checkout::address::delete); }); route .post("/payment_details") .to(checkout::payment_details::create); route .put("/payment_details") .to(checkout::payment_details::update); route.post("/complete").to(checkout::complete); }); route.scope("/api", |route| { route.get("/products").to(api::products::index); }); }) } /// Start a server and use a `Router` to dispatch requests pub fn main() { let addr = "127.0.0.1:7878"; println!("Listening for requests at http://{}", addr); gotham::start(addr, router()) } #[cfg(test)] mod tests { use super::*; use gotham::test::TestServer; use hyper::StatusCode; // A small subset of possible tests #[test] fn index_get() { let test_server = TestServer::new(router()).unwrap(); let response = test_server .client() .get("http://localhost") .perform() .unwrap(); assert_eq!(response.status(), StatusCode::Ok); let body = response.read_body().unwrap(); assert_eq!(&body[..], b"index"); } #[test] fn checkout_address_patch() { let test_server = TestServer::new(router()).unwrap(); let response = test_server .client() .patch( "http://localhost/checkout/address", "data", mime::TEXT_PLAIN, ) .perform() .unwrap(); assert_eq!(response.status(), StatusCode::Ok); let body = response.read_body().unwrap(); assert_eq!(&body[..], b"update"); } } ``` -------------------------------- ### Start Server with Defined Router Source: https://gotham.rs/ Starts a Gotham server on `127.0.0.1:7878` using the previously defined `router()` function to handle incoming requests. ```rust /// Start a server and call the `Handler` we've defined above /// for each `Request` we receive. pub fn main() { let addr = "127.0.0.1:7878"; println!("Listening for requests at http://{}", addr); gotham::start(addr, router()) } ``` -------------------------------- ### Build Router with State Middleware Source: https://gotham.rs/ Constructs a router with a `StateMiddleware` to share a `RequestCounter` across handlers. It defines a GET route at `/` that maps to the `say_hello` handler. ```rust /// Constructs a simple router on `/` to say hello, along with /// the current request count. fn router() -> Router { // create the counter to share across handlers let counter = RequestCounter::new(); // create our state middleware to share the counter let middleware = StateMiddleware::new(counter); // create a middleware pipeline from our middleware 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 build_router(chain, pipelines, |route| { route.get("/").to(say_hello); }) } ``` -------------------------------- ### Build Router with Query String Extractor Source: https://gotham.rs/ Configure the router to use `with_query_string_extractor` for the `/products` GET route, specifying the `QueryStringExtractor` struct. ```rust /// Create a `Router` /// /// /products?name=... --> GET fn router() -> Router { build_simple_router(|route| { route .get("/products") // This tells the Router that for requests which match this route that query string // extraction should be invoked storing the result in a `QueryStringExtractor` instance. .with_query_string_extractor::() .to(get_product_handler); }) } ``` -------------------------------- ### Setup Diesel Middleware in Gotham Source: https://gotham.rs/blog Initialize and configure the Diesel middleware for database interactions within a Gotham application. This involves creating a Repo and integrating it into the middleware pipeline. ```rust let repo: Repo = Repo::new("products.db"); let pipeline = single_middleware(DieselMiddleware::new(repo)); let (chain, pipelines) = single_pipeline(pipeline); gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| { route.get("/").to(say_hello); })) ``` -------------------------------- ### Implement Gotham Middleware Source: https://gotham.rs/ Implement the `Middleware` trait to create custom middleware. This example shows how to access request headers, add data to the state, and modify the response. ```rust use gotham::middleware::Middleware; use gotham::pipeline::Chain; use gotham::state::{FromState, State}; use hyper::{HeaderMap, Response, StatusCode}; use std::future::Future; /// A struct that can act as a Gotham web framework middleware. #[derive(Clone, NewMiddleware)] pub struct ExampleMiddleware; /// Implementing `gotham::middleware::Middleware` allows the logic that you want your Middleware to /// provided to be correctly executed by the Gotham web framework Router. impl Middleware for ExampleMiddleware { fn call(self, mut state: State, chain: Chain) -> Box where Chain: FnOnce(State) -> Box, { let user_agent = { let headers = HeaderMap::borrow_from(&state); match headers.get("user-agent") { Some(ua) => ua.to_str().unwrap().into(), None => String::from("None"), } }; // Prior to letting Request handling proceed our middleware creates some new data and adds // it to `state`. state.put(ExampleMiddlewareData { user_agent, supported: false }); // We're finished working on the Request, so allow other components to continue processing // the Request. let result = chain(state); // Once a Response is generated by another part of the application, in this example's case // the middleware_reliant_handler function, we want to do some more work. let f = result.and_then(move |(state, mut response)| { { let headers = response.headers_mut(); let data = ExampleMiddlewareData::borrow_from(&state); // All our middleware does is add a header to the Response generated by our handler. headers.insert( "X-User-Agent", format!( "Supplied: {}, Supported: {}", data.user_agent, data.supported ) .parse() .unwrap(), ); }; future::ok((state, response)) }); Box::new(f) } } ``` -------------------------------- ### Integrate Gotham with Custom Hyper Services Source: https://gotham.rs/blog/release/2021/03/20/gotham-0.6.html Implement a custom Hyper service by defining a struct that holds the router and address, and then implementing the `Service` trait. This allows for more customization than Gotham's default server start methods. ```rust #[derive(Clone)] struct MyService { router: Router, addr: SocketAddr, } impl Service> for MyService { type Response = Response; type Error = Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> task::Poll> { task::Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { // NOTE: You don't *have* to use call_handler for this (you could use `router.handle`), but // call_handler will catch panics and return en error response. let state = State::from_request(req, self.addr); call_handler(self.router.clone(), AssertUnwindSafe(state)).boxed() } } let addr = "127.0.0.1:7878"; let listener = TcpListener::bind(&addr).await?; loop { let (socket, addr) = listener .accept() .await .context("Error accepting connection")?; let service = MyService { router: router.clone(), addr, }; let task = async move { Http::new() .serve_connection(socket, service) .await .context("Error serving connection")?; Result::<_, Error>::Ok(()) }; tokio::spawn(task); } ``` -------------------------------- ### Test Product Extraction Source: https://gotham.rs/ Write a test case using `TestServer` to send a GET request to `/products?name=t-shirt`, assert the status code is OK, and verify the response body contains the correctly serialized `Product`. ```rust #[cfg(test)] mod tests { use super::*; use gotham::test::TestServer; #[test] fn product_name_is_extracted() { let test_server = TestServer::new(router()).unwrap(); let response = test_server .client() .get("http://localhost/products?name=t-shirt") .perform() .unwrap(); assert_eq!(response.status(), StatusCode::Ok); let body = response.read_body().unwrap(); let expected_product = Product { name: "t-shirt".to_string(), }; let expected_body = serde_json::to_string(&expected_product).expect("serialized product"); assert_eq!(&body[..], expected_body.as_bytes()); } } ``` -------------------------------- ### Implement Request Counter with StateMiddleware in Rust Source: https://gotham.rs/blog This example shows how to create a mutable shared state (RequestCounter) using Arc and Mutex, making it cloneable and StateData compatible. It's then integrated into a Gotham server to track request counts across multiple threads. ```rust /// Request counter, used to track the number of requests made. /// /// Due to being shared across many worker threads, the internal counter /// is bound inside an `Arc` (to enable sharing) and a `Mutex` (to enable /// modification from multiple threads safely). /// /// This struct must implement `Clone` and `StateData` to be applicable /// for use with the `StateMiddleware`, and be shared via `Middleware`. #[derive(Clone, StateData)] struct RequestCounter { inner: Arc>, } /// Counter implementation. impl RequestCounter { /// Creates a new request counter, setting the base state to `0`. fn new() -> Self { Self { inner: Arc::new(Mutex::new(0)), } } /// Increments the internal counter state by `1`, and returns the /// new request counter as an atomic operation. fn incr(&self) -> usize { let mut w = self.inner.lock().unwrap(); *w += 1; *w } } /// Basic `Handler` to say hello and return the current request count. /// /// The request counter is shared via the state, so we can safely /// borrow one from the provided state. As the counter uses locks /// internally, we don't have to borrow a mutable reference either! fn say_hello(state: State) -> (State, Response) { let message = { // borrow a reference to the counter from the state let counter = RequestCounter::borrow_from(&state); // create our message, incrementing our request counter format!("Hello from request #{}!\n", counter.incr()) }; // create the response let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, message); // done! (state, res) } /// Start a server and call the `Handler` we've defined above /// for each `Request` we receive. pub fn main() { // create our state middleware to share the counter let middleware = StateMiddleware::new(RequestCounter::new()); // create a middleware pipeline from our middleware 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); })) } ``` -------------------------------- ### Integrate Gotham into Custom Hyper Services Source: https://gotham.rs/blog Implement a custom Hyper service by embedding a Gotham router. This allows for greater customization compared to Gotham's default server start methods. Ensure proper handling of requests and responses within the `call` method. ```rust #[derive(Clone)] struct MyService { router: Router, addr: SocketAddr, } impl Service> for MyService { type Response = Response; type Error = Error; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> task::Poll> { task::Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { // NOTE: You don't *have* to use call_handler for this (you could use `router.handle`), but // call_handler will catch panics and return en error response. let state = State::from_request(req, self.addr); call_handler(self.router.clone(), AssertUnwindSafe(state)).boxed() } } let addr = "127.0.0.1:7878"; let listener = TcpListener::bind(&addr).await?; loop { let (socket, addr) = listener .accept() .await .context("Error accepting connection")?; let service = MyService { router: router.clone(), addr, }; let task = async move { Http::new() .serve_connection(socket, service) .await .context("Error serving connection")?; Result::<_, Error>::Ok(()) }; tokio::spawn(task); } ``` -------------------------------- ### Rust: Serve Directory with Custom Options Source: https://gotham.rs/blog Illustrates serving a directory of assets with customized options for compression and cache control. ```rust route.get("/static").to_dir( FileOptions::new("assets") .with_cache_control("no-cache") .with_gzip(true) .build(), ); ``` -------------------------------- ### Rust: Serve Directory Async Source: https://gotham.rs/blog Demonstrates exposing all assets within a directory using the `to_dir` method on a route. ```rust route.get("/static").to_dir("assets"); ``` -------------------------------- ### Create Gotham Router with Pipeline Source: https://gotham.rs/ Demonstrates how to create a Gotham `Router` and configure a middleware pipeline. Pipelines define the order in which middleware is executed. ```rust use gotham::pipeline::new_pipeline; use gotham::router::builder::*; use gotham::router::Router; /// Create a `Router` fn router() -> Router { // Within the Gotham web framework Middleware is added to and referenced from a Pipeline. // // A pipeline can consist of multiple Middleware types and guarantees to call them all in the // ordering which is established by successive calls to the `add` method. let pipeline = new_pipeline().add(ExampleMiddleware).build(); build_router(pipeline, |route| { route.get("/").to(middleware_reliant_handler) }) } ``` -------------------------------- ### Initialize and Use Diesel Middleware Source: https://gotham.rs/blog/release/2019/07/14/gotham-0.4.html Set up Diesel middleware by creating a Repo, initializing the middleware, and integrating it into the Gotham pipeline and router. Access the Repo from the request state for database operations. ```rust let repo: Repo = Repo::new("products.db"); let pipeline = single_middleware(DieselMiddleware::new(repo)); let (chain, pipelines) = single_pipeline(pipeline); gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| { route.get("/").to(say_hello); })) ``` ```rust let repo = Repo::borrow_from(&state); repo.run(move |conn| { diesel::insert_into(products::table) .values(&product) .execute(&conn) }) ``` -------------------------------- ### Customize Gotham Server Startup Source: https://gotham.rs/blog/release/2018/10/29/gotham-0.3.html Gotham 0.3 offers multiple APIs to control server startup, including specifying the number of threads or a custom executor. These APIs delegate to each other, providing flexibility with minimal overhead. ```rust // Same as previous; uses default threads + runtime gotham::start("127.0.0.1:7878", router()); // Allows control of thread counts; in this case a single thread gotham::start_with_num_threads("127.0.0.1:7878", router(), 1); // Allows control of the `TaskExecutor` used gotham::start_on_executor("127.0.0.1:7878", executor()); // Returns a `Future` to spawn a Gotham server gotham::init_server("127.0.0.1:7878", router()); ``` -------------------------------- ### Async Static File Serving with to_file Source: https://gotham.rs/blog/release/2018/10/29/gotham-0.3.html Exposes a single static asset at a given route using the `to_file` method. Ensure the file path is correct. ```rust build_simple_router(|route| { // You can use `to_file` on a route to expose a single asset: route.get("/").to_file("assets/index.html"); // Alternatively, you can use `to_dir` to expose a directory of assets: route.get("/static").to_dir("assets"); // Finally, you can customize options for compression, cache control, etc: route.get("/static").to_dir( FileOptions::new("assets") .with_cache_control("no-cache") .with_gzip(true) .build(), ); }); ``` -------------------------------- ### Build Router with Custom Pipelines Source: https://gotham.rs/ Use `build_router` directly when you need to introduce custom Pipelines and Middleware. Switching from `build_simple_router` is straightforward. ```rust // A pipeline is considered complete once the build method is called and can no longer // be modified. // // The Gotham web framework supports multiple Pipelines and even Pipelines containing Pipelines. // However, as shown here, many applications will get sufficent power and flexibility // from a `single_pipeline` which we've provided specific API assitance for. let (chain, pipelines) = single_pipeline(new_pipeline().add(ExampleMiddleware).build()); // Notice we've switched from build_simple_router which has been present in all our examples up // until this point. Under the hood build_simple_router has simply been creating an empty // set of Pipelines on your behalf. // // Now that we're creating and populating our own Pipelines we'll switch to using // build_router directly. // // Tip: Use build_simple_router for as long as you can. Switching to build_router is simple once // do need to introduce Pipelines and Middleware. build_router(chain, pipelines, |route| { route.get("/").to(middleware_reliant_handler); }) ``` -------------------------------- ### Implement Request Handler Source: https://gotham.rs/ Implement a handler function that extracts query parameters using `QueryStringExtractor::take_from`, creates a `Product`, and returns a JSON response. Ensure the `QueryStringExtractor` is derived with `StateData` and `StaticResponseExtender`. ```rust /// Handler function for `GET` requests directed to `/products` /// /// This handler uses the Serde project when generating responses. You don't need to /// know about Serde in order to understand the response that is being created here but if you're /// interested you can learn more at `http://serde.rs`. fn get_product_handler(mut state: State) -> (State, Response) { let res = { // Access the `QueryStringExtractor` instance from `state` which was put there for us by the // `Router` during request evaluation. // // As well as permitting storage in `State` by deriving `StateData` our query string // extractor struct automatically gains the `take_from` method and a number of other // methods via the `gotham::state::FromState` trait. // // n.b. Once taken out of `state` values can no longer be accessed by other application // code or middlewares. let query_param = QueryStringExtractor::take_from(&mut state); let product = Product { name: query_param.name, }; create_response( &state, StatusCode::OK, mime::APPLICATION_JSON, serde_json::to_vec(&product).expect("serialized product"), ) }; (state, res) } ``` -------------------------------- ### Initialize and Use JWT Middleware Source: https://gotham.rs/blog/release/2019/07/14/gotham-0.4.html Configure JWT middleware by defining a Claims struct, creating the middleware with a secret, and integrating it into the Gotham pipeline and router. Access the deserialized Claims from the request state. ```rust #[derive(Deserialize, Debug)] struct Claims { sub: String, exp: usize } let middleware = JWTMiddleware::::new("secret".as_ref()); let pipeline = single_middleware(middleware); let (chain, pipelines) = single_pipeline(pipeline); gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| { route.get("/").to(say_hello); })) ``` ```rust let claims = Claims::borrow_from(&state); ``` -------------------------------- ### Rust: Simple Hello World Response Source: https://gotham.rs/blog Demonstrates returning a static string response using the IntoResponse trait. This works because &'static str implements Into. ```rust const HELLO_WORLD: &'static str = "Hello, world!"; pub fn say_hello(state: State) -> (State, &'static str) { (state, HELLO_WORLD) } pub fn main() { gotham::start("127.0.0.1:7878", || Ok(say_hello)) } ``` -------------------------------- ### Create Handler to Say Hello with Request Count Source: https://gotham.rs/ A basic `Handler` that formats a greeting message including the current request count, safely borrowed from the state. The counter is incremented atomically. ```rust /// Basic `Handler` to say hello and return the current request count. /// /// The request counter is shared via the state, so we can safely /// borrow one from the provided state. As the counter uses locks /// internally, we don't have to borrow a mutable reference either! fn say_hello(state: State) -> (State, String) { let message = { // borrow a reference of the counter from the state let counter = RequestCounter::borrow_from(&state); // create our message, incrementing our request counter format!("Hello from request #{}!\n", counter.incr()) }; // return message (state, message) } ``` -------------------------------- ### Rust: Tuple Response Types Source: https://gotham.rs/blog Illustrates using tuple structures to control response status codes and mime types when returning responses. ```rust (mime::Mime, Into) ``` ```rust (hyper::StatusCode, mime::Mime, Into) ``` -------------------------------- ### Async/Await Handler with Borrowing and ? Operator Source: https://gotham.rs/blog/release/2020/09/06/gotham-0.5.html Handlers can now borrow mutable state and use the `?` operator for cleaner error handling with `anyhow::Result`. Use `.to_async_borrowing` for this pattern. ```rust async fn foo_create_response() -> anyhow::Result> { unimplemented!() } async fn foo_handler(state: &mut State) -> Result, HandlerError> { let res = foo_create_response().await?; Ok(res) } fn router() -> Router { build_simple_router(|router| { router.get("/foo").to_async_borrowing(foo_handler); }) } ``` -------------------------------- ### Execute Database Calls with Diesel Repo Source: https://gotham.rs/blog Access and utilize the Diesel Repo from the request state to perform database operations asynchronously. The `repo.run` method returns a Future, allowing seamless integration with other async code. ```rust let repo = Repo::borrow_from(&state); repo.run(move |conn| { diesel::insert_into(products::table) .values(&product) .execute(&conn) }) ``` -------------------------------- ### Define Product Struct Source: https://gotham.rs/ Define a simple struct to represent the data that will be serialized into a JSON response. ```rust /// A Product #[derive(Serialize)] struct Product { name: String, } ``` -------------------------------- ### Configure CookieParser Middleware in Gotham Source: https://gotham.rs/blog/release/2019/07/14/gotham-0.4.html Set up the CookieParser middleware in your Gotham router. This makes a CookieJar available on the request State. ```rust // 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); })) ``` -------------------------------- ### Shorthand Response with &'static str Source: https://gotham.rs/blog/release/2018/10/29/gotham-0.3.html Demonstrates returning a static string as a response using the IntoResponse trait. This works because &'static str implements Into. ```rust const HELLO_WORLD: &'static str = "Hello, world!"; pub fn say_hello(state: State) -> (State, &'static str) { (state, HELLO_WORLD) } pub fn main() { gotham::start("127.0.0.1:7878", || Ok(say_hello)) } ``` -------------------------------- ### Test Server Request Loop Source: https://gotham.rs/ This test verifies that the server responds correctly to multiple requests by checking the status code and body content for each request. It ensures that the response increments as expected. ```rust use gotham::test::TestServer; use hyper::StatusCode; #[test] fn receive_incrementing_hello_response() { let test_server = TestServer::new(router()).unwrap(); for i in 1..6 { let response = test_server .client() .get("http://localhost") .perform() .unwrap(); assert_eq!(response.status(), StatusCode::OK); let body = response.read_body().unwrap(); let expc = format!("Hello from request #{}!\n", i); assert_eq!(&body[..], expc.as_bytes()); } } ``` -------------------------------- ### Router with Multiple Named Globs Source: https://gotham.rs/blog/release/2020/09/06/gotham-0.5.html Define routes that capture multiple named segments using glob patterns. This allows for more flexible URL structures. ```rust fn router() -> Router { build_simple_router(|router| { router.get("/foo/*first/bar/*second").with_path_extractor::().to(foo_bar_handler); }) } ``` -------------------------------- ### Test Server Middleware and Handler Collaboration Source: https://gotham.rs/ Tests that middleware and handlers collaborate correctly by making a request to a test server and asserting the response status and a specific header set by the middleware. ```rust #[test] fn ensure_middleware_and_handler_collaborate() { let test_server = TestServer::new(router()).unwrap(); let response = test_server .client() .get("http://localhost") .with_header(UserAgent::new("TestServer/0.0.0")) .perform() .unwrap(); assert_eq!(response.status(), StatusCode::Ok); // Ensure Middleware has set a header after our handler generated the Response. assert_eq!( response.headers().get_raw("X-User-Agent").unwrap(), "Supplied: TestServer/0.0.0, Supported: true" ); } ``` -------------------------------- ### Async/Await Handler in Gotham Source: https://gotham.rs/blog/release/2020/09/06/gotham-0.5.html Use true async functions as handlers with the `.to_async` method. This requires the handler function to return a `Response` and can be awaited. ```rust async fn foo_create_response() -> Response { unimplemented!() } async fn foo_handler(state: State) -> HandlerResult { let res = foo_create_response().await; Ok((state, res)) } fn router() -> Router { build_simple_router(|router| { route.get("/foo").to_async(foo_handler); }) } ``` -------------------------------- ### Gotham Extractor Introduction Source: https://gotham.rs/ This snippet introduces the concept of extracting query string name/value pairs in a type-safe manner within the Gotham web framework. ```rust //! An introduction to extracting query string name/value pairs, in a type safe way, with the //! Gotham web framework #[macro_use] extern crate gotham_derive; #[macro_use] ``` -------------------------------- ### Access CookieJar from State Source: https://gotham.rs/blog/release/2019/07/14/gotham-0.4.html Retrieve the CookieJar from the request State after the CookieParser middleware has been configured. ```rust CookieJar::borrow_from(&state) ``` -------------------------------- ### Configure JWT Middleware in Gotham Source: https://gotham.rs/blog Set up JWT authentication middleware in Gotham by defining a Claims structure and initializing the JWTMiddleware with a secret. This middleware automatically validates tokens from the Authorization header. ```rust #[derive(Deserialize, Debug)] struct Claims { sub: String, exp: usize } let middleware = JWTMiddleware::::new("secret".as_ref()); let pipeline = single_middleware(middleware); let (chain, pipelines) = single_pipeline(pipeline); gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| { route.get("/").to(say_hello); })) ``` -------------------------------- ### Implement Shared State with StateMiddleware Source: https://gotham.rs/blog/release/2018/10/29/gotham-0.3.html Use `StateMiddleware` to share mutable state like a request counter across requests. Ensure your state struct implements `Clone` and `StateData`, and is wrapped in `Arc>` for thread-safe access. Dependencies and use clauses are omitted for brevity. ```rust /// Request counter, used to track the number of requests made. /// /// Due to being shared across many worker threads, the internal counter /// is bound inside an `Arc` (to enable sharing) and a `Mutex` (to enable /// modification from multiple threads safely). /// /// This struct must implement `Clone` and `StateData` to be applicable /// for use with the `StateMiddleware`, and be shared via `Middleware`. #[derive(Clone, StateData)] struct RequestCounter { inner: Arc>, } /// Counter implementation. impl RequestCounter { /// Creates a new request counter, setting the base state to `0`. fn new() -> Self { Self { inner: Arc::new(Mutex::new(0)), } } /// Increments the internal counter state by `1`, and returns the /// new request counter as an atomic operation. fn incr(&self) -> usize { let mut w = self.inner.lock().unwrap(); *w += 1; *w } } /// Basic `Handler` to say hello and return the current request count. /// /// The request counter is shared via the state, so we can safely /// borrow one from the provided state. As the counter uses locks /// internally, we don't have to borrow a mutable reference either! fn say_hello(state: State) -> (State, Response) { let message = { // borrow a reference to the counter from the state let counter = RequestCounter::borrow_from(&state); // create our message, incrementing our request counter format!("Hello from request #{}\n", counter.incr()) }; // create the response let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, message); // done! (state, res) } /// Start a server and call the `Handler` we've defined above /// for each `Request` we receive. pub fn main() { // create our state middleware to share the counter let middleware = StateMiddleware::new(RequestCounter::new()); // create a middleware pipeline from our middleware 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); })) } ``` -------------------------------- ### Define Query String Extractor Struct Source: https://gotham.rs/ Define a struct that derives `Deserialize`, `StateData`, and `StaticResponseExtender` to extract data from the request's query string. Field names must match query string parameter names. ```rust extern crate serde_derive; use hyper::{Body, Response, StatusCode}; use gotham::helpers::http::response::create_response; use gotham::router::{builder::*, Router}; use gotham::state::{FromState, State}; /// Holds data extracted from the Request query string. /// /// When a query string extraction struct is configured for a route as part of `Router` creation /// the `Router` will attempt to extract data from each matching request's query string and store /// it in `state` ready for your application to use, ensuring that all type safety requirements have /// been met by the request before handing back control. /// /// The key requirements for struct to act as a query string extractor are: /// /// 1. That the struct implements the `serde::de::Deserialize` trait which we're doing here by /// simply deriving it. The Gotham router uses this property during Request query string /// evaluation to create and instance of your struct, populate it and store it into state /// ready for access by application code. /// 2. That the struct implements `gotham::state::data::StateData` trait so that it can be /// stored, retrieved and removed from state. You generally get this for free by deriving /// `StateData` as shown here. /// 3. That the struct implements the /// `gotham::router::response::extender::StaticResponseExtender` trait so that bad request /// query string data can be appropriately refused by the Router. You generally get this /// for free by deriving `StaticResponseExtender` as shown here which results in bad /// requests being refuted with a HTTP 400 (BadRequest) response status code. /// /// Naming of fields in extraction structs is important, the same names must appear in the /// query string. #[derive(Deserialize, StateData, StaticResponseExtender)] struct QueryStringExtractor { name: String, } ``` -------------------------------- ### Define RequestCounter for State Sharing Source: https://gotham.rs/ Defines a `RequestCounter` struct to safely share and modify state across multiple worker threads using `Arc` and `Mutex`. It must implement `Clone` and `StateData` for use with `StateMiddleware`. ```rust /// Request counting struct, used to track the number of requests made. /// /// Due to being shared across many worker threads, the internal counter /// is bound inside an `Arc` (to enable sharing) and a `Mutex` (to enable /// modification from multiple threads safely). /// /// This struct must implement `Clone` and `StateData` to be applicable /// for use with the `StateMiddleware`, and be shared via `Middleware`. #[derive(Clone, StateData)] struct RequestCounter { inner: Arc>, } /// Counter implementation. impl RequestCounter { /// Creates a new request counter, setting the base state to `0`. fn new() -> Self { Self { inner: Arc::new(Mutex::new(0)), } } /// Increments the internal counter state by `1`, and returns the /// new request request counter as an atomic operation. fn incr(&self) -> usize { let mut w = self.inner.lock().unwrap(); *w += 1; *w } } ``` -------------------------------- ### Access JWT Claims from State Source: https://gotham.rs/blog Retrieve deserialized JWT claims from the request state within Gotham API handlers. The `Claims` structure is automatically populated by the JWTMiddleware. ```rust let claims = Claims::borrow_from(&state); ``` -------------------------------- ### Handler Reliant on Middleware Data Source: https://gotham.rs/ A handler function that accesses and modifies data previously placed in the state by middleware. This demonstrates the interaction between middleware and handlers. ```rust use gotham::state::{FromState, State}; use hyper::{Body, Response, StatusCode}; /// The handler which is invoked for all requests to "/". /// /// This handler expects that `ExampleMiddleware` has already been executed by Gotham before /// it is invoked. As a result of that middleware being run our handler trusts that it must /// have placed data into state that we can perform operations on. pub fn middleware_reliant_handler(mut state: State) -> (State, Response) { { let data = ExampleMiddlewareData::borrow_mut_from(&mut state); // Mark any kind of web client as supported. A trival example but it highlights the // interaction that is possible between Middleware and Handlers via state. data.supported = true; }; // Finally we create a basic Response to complete our handling of the Request. let res = create_empty_response(&state, StatusCode::OK); (state, res) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.