Skip to main content

larust_http/
request.rs

1//! Laravel's `Illuminate\Http\Request`, scoped to what it's for here:
2//! reading headers without a handler having to know axum's `HeaderMap`
3//! API. Route/query parameters deliberately stay on their own
4//! `Path<T>`/`Query<T>` extractors rather than being folded into this
5//! type - Laravel's untyped `$request->route('id')` is exactly what
6//! `Path<T>`'s compile-time-typed, auto-rejecting alternative already
7//! replaces the need for (see `routes/api.rs`'s doc comment).
8
9use axum::extract::FromRequestParts;
10use axum::http::request::Parts;
11use axum::http::HeaderMap;
12use std::collections::BTreeMap;
13use std::convert::Infallible;
14
15pub struct Request {
16    headers: HeaderMap,
17}
18
19impl Request {
20    /// Laravel's `$request->header('X-Foo')` - `None` if the header is
21    /// absent *or* isn't valid UTF-8 (Laravel's own string-typed `header()`
22    /// would choke on that case too, just later and less clearly).
23    pub fn header(&self, name: &str) -> Option<&str> {
24        self.headers.get(name)?.to_str().ok()
25    }
26
27    /// Laravel's `$request->headers->all()`, flattened to strings the same
28    /// way `header()` is - a non-UTF-8 value falls back to a placeholder
29    /// instead of failing the whole map.
30    pub fn headers(&self) -> BTreeMap<String, String> {
31        self.headers
32            .iter()
33            .map(|(name, value)| {
34                let value = value.to_str().unwrap_or("<non-utf8>").to_string();
35                (name.to_string(), value)
36            })
37            .collect()
38    }
39}
40
41// GOTCHAS.md: axum-core declares `FromRequestParts` via `#[async_trait]`,
42// not native async-fn-in-traits - an impl written as a plain `async fn`
43// fails with a confusing E0195 lifetime error instead of a clear message
44// about the mismatch.
45#[axum::async_trait]
46impl<S> FromRequestParts<S> for Request
47where
48    S: Send + Sync,
49{
50    type Rejection = Infallible;
51
52    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
53        Ok(Request {
54            headers: parts.headers.clone(),
55        })
56    }
57}