Skip to main content

Module validate_request

Module validate_request 

Source
Available on crate feature validate-request only.
Expand description

Middleware that validates requests.

§Example

Validation of the Accept header can be made by using ValidateRequestHeaderLayer::accept():

use tower_http::validate_request::ValidateRequestHeaderLayer;
use http::{Request, Response, StatusCode, header::ACCEPT};
use http_body_util::Full;
use bytes::Bytes;
use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}

let mut service = ServiceBuilder::new()
    // Require the `Accept` header to be `application/json`, `*/*` or `application/*`
    .layer(ValidateRequestHeaderLayer::accept("application/json"))
    .service_fn(handle);

// Requests with the correct value are allowed through
let request = Request::builder()
    .header(ACCEPT, "application/json")
    .body(Full::default())
    .unwrap();

let response = service
    .ready()
    .await?
    .call(request)
    .await?;

assert_eq!(StatusCode::OK, response.status());

// Requests with an invalid value get a `406 Not Acceptable` response
let request = Request::builder()
    .header(ACCEPT, "text/strings")
    .body(Full::default())
    .unwrap();

let response = service
    .ready()
    .await?
    .call(request)
    .await?;

assert_eq!(StatusCode::NOT_ACCEPTABLE, response.status());

Validation of a custom header can be made by using ValidateRequestHeaderLayer::has_header_value():

use tower_http::validate_request::ValidateRequestHeaderLayer;
use http::{Request, Response, StatusCode};
use http_body_util::Full;
use bytes::Bytes;
use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}

let mut service = ServiceBuilder::new()
    // Require a `X-Custom-Header` header to have the value `random-value-1234567890` or reject with a `403 Forbidden` response
    .layer(ValidateRequestHeaderLayer::has_header_value(
        "x-custom-header",
        "random-value-1234567890",
    ).expect("invalid validate header"))
    .service_fn(handle);

// Requests with the correct value are allowed through
let request = Request::builder()
    .header("x-custom-header", "random-value-1234567890")
    .body(Full::default())
    .unwrap();

let response = service
    .ready()
    .await?
    .call(request)
    .await?;

assert_eq!(StatusCode::OK, response.status());

// Requests with an invalid value get a `403 Forbidden` response
let request = Request::builder()
    .header("x-custom-header", "wrong-value")
    .body(Full::default())
    .unwrap();

let response = service
    .ready()
    .await?
    .call(request)
    .await?;

assert_eq!(StatusCode::FORBIDDEN, response.status());

To require only that a header is present, use ValidateRequestHeaderLayer::custom():

use tower_http::validate_request::ValidateRequestHeaderLayer;
use http::{Request, Response, StatusCode};
use http_body_util::Full;
use bytes::Bytes;
use tower::{ServiceBuilder, service_fn, BoxError};

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}

let service = ServiceBuilder::new()
    .layer(ValidateRequestHeaderLayer::custom(|req: &mut Request<Full<Bytes>>| {
        if req.headers().contains_key("x-custom-header") {
            Ok(())
        } else {
            let mut res = Response::new(Full::<Bytes>::default());
            *res.status_mut() = StatusCode::FORBIDDEN;
            Err(res)
        }
    }))
    .service_fn(handle);

To serve a custom response when validation fails, also use ValidateRequestHeaderLayer::custom():

use tower_http::validate_request::ValidateRequestHeaderLayer;
use http::{Request, Response, StatusCode};
use http_body_util::Full;
use bytes::Bytes;
use tower::{ServiceBuilder, service_fn, BoxError};

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}

let service = ServiceBuilder::new()
    .layer(ValidateRequestHeaderLayer::custom(|req: &mut Request<Full<Bytes>>| {
        match req.headers().get("x-custom-header").map(|v| v.as_bytes()) {
            Some(b"random-value-1234567890") => Ok(()),
            _ => Err(Response::builder()
                .status(StatusCode::FORBIDDEN)
                .body(Full::<Bytes>::default())
                .unwrap()),
        }
    }))
    .service_fn(handle);

Custom validation can be made by implementing ValidateRequest:

use tower_http::validate_request::{ValidateRequestHeaderLayer, ValidateRequest};
use http::{Request, Response, StatusCode, header::ACCEPT};
use http_body_util::Full;
use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};
use bytes::Bytes;

#[derive(Clone, Copy)]
pub struct MyHeader { /* ...  */ }

impl<B> ValidateRequest<B> for MyHeader {
    type ResponseBody = Full<Bytes>;

    fn validate(
        &mut self,
        request: &mut Request<B>,
    ) -> Result<(), Response<Self::ResponseBody>> {
        // validate the request...
    }
}

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}


let service = ServiceBuilder::new()
    // Validate requests using `MyHeader`
    .layer(ValidateRequestHeaderLayer::custom(MyHeader { /* ... */ }))
    .service_fn(handle);

Structs§

AcceptHeader
Type that performs validation of the Accept header.
RequiredHeaderValue
Type that rejects requests if a header is not present or does not have an expected value.
ResponseFuture
Response future for ValidateRequestHeader.
ValidateRequestHeader
Middleware that validates requests.
ValidateRequestHeaderLayer
Layer that applies ValidateRequestHeader which validates all requests.

Traits§

ValidateRequest
Trait for validating requests.