diff --git a/Cargo.lock b/Cargo.lock index 64b13fe8..d6124a0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2318,6 +2318,13 @@ dependencies = [ "thiserror", ] +[[package]] +name = "guards" +version = "1.0.0" +dependencies = [ + "actix-web", +] + [[package]] name = "h2" version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index df9c7cd5..b3349b3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "graphql/async-graphql", "graphql/juniper-advanced", "graphql/juniper", + "guards", "http-proxy", "https-tls/awc-https", "https-tls/openssl-auto-le", diff --git a/guards/Cargo.toml b/guards/Cargo.toml new file mode 100644 index 00000000..bcdb9d7c --- /dev/null +++ b/guards/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "guards" +version = "1.0.0" +edition = "2021" + +[dependencies] +actix-web = "4" diff --git a/guards/README.md b/guards/README.md new file mode 100644 index 00000000..aa684da6 --- /dev/null +++ b/guards/README.md @@ -0,0 +1,34 @@ +# guards + +Shows how to set up custom routing guards. +- Routing different API versions using a header instead of path. + +## Usage + +### Running The Server + +```sh +cd guards +cargo run --bin=guards +``` + +### Available Routes + +#### `GET /api/hello` + +Requires the `Accept-Version` header to be present and set to `1` or `2`. + +Using [HTTPie]: + +```sh +http :8080/api/hello Accept-Version:1 +``` + +Using [cURL]: + +```sh +curl 'localhost:8080/api/hello' -H 'accept-version: 1' +``` + +[HTTPie]: https://httpie.org +[cURL]: https://curl.haxx.se diff --git a/guards/src/main.rs b/guards/src/main.rs new file mode 100644 index 00000000..059a9d68 --- /dev/null +++ b/guards/src/main.rs @@ -0,0 +1,73 @@ +use actix_web::{ + body::MessageBody, + dev::{ServiceFactory, ServiceRequest, ServiceResponse}, + get, + guard::{Guard, GuardContext}, + middleware::DefaultHeaders, + web, App, Error, HttpServer, Responder, +}; + +mod v1 { + use super::*; + + pub struct ApiGuard; + + impl Guard for ApiGuard { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + ctx.head() + .headers() + .get("Accept-Version") + .map_or(false, |hv| hv.as_bytes() == b"1") + } + } + + #[get("/hello")] + pub async fn hello() -> impl Responder { + "Hello World from v1 API!" + } +} + +mod v2 { + use super::*; + + pub struct ApiGuard; + + impl Guard for ApiGuard { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + ctx.head() + .headers() + .get("Accept-Version") + .map_or(false, |hv| hv.as_bytes() == b"2") + } + } + + #[get("/hello")] + pub async fn hello() -> impl Responder { + "Hello World from the awesome new v2 API!" + } +} + +fn create_app() -> App< + impl ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Config = (), + InitError = (), + Error = Error, + >, +> { + App::new() + .service(web::scope("/api").guard(v1::ApiGuard).service(v1::hello)) + .service(web::scope("/api").guard(v2::ApiGuard).service(v2::hello)) + // using this form of API version selection means that we need to send a Vary header so that + // caches won't try to serve the wrong response + .wrap(DefaultHeaders::new().add(("Vary", "Accept-Version"))) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(create_app) + .bind(("127.0.0.1", 8080))? + .run() + .await +} diff --git a/shutdown-server/README.md b/shutdown-server/README.md index f56076df..0859aae9 100644 --- a/shutdown-server/README.md +++ b/shutdown-server/README.md @@ -12,7 +12,7 @@ Demonstrates how to shutdown the web server in a couple of ways: ### Running The Server ```sh -cd basics/shutdown-server +cd shutdown-server cargo run --bin shutdown-server # Starting 8 workers