1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/*!
# Trillium method override handler
This allows http clients that are unable to generate http methods
other than `GET` and `POST` to use `POST` requests that are
interpreted as other methods such as `PUT`, `PATCH`, or `DELETE`.
This is currently supported with a querystring parameter of
`_method`. To change the querystring parameter's name, use
[`MethodOverride::with_param_name`]
By default, the only methods allowed are `PUT`, `PATCH`, and
`DELETE`. To override this, use
[`MethodOverride::with_allowed_methods`]
Subsequent handlers see the requested method on the conn instead of
POST.
*/
#![forbid(unsafe_code)]
#![deny(
missing_copy_implementations,
rustdoc::missing_crate_level_docs,
missing_debug_implementations,
missing_docs,
nonstandard_style,
unused_qualifications
)]
use querystrong::QueryStrong;
use std::{collections::HashSet, fmt::Debug};
use trillium::{async_trait, conn_unwrap, Conn, Handler, Method};
/**
Trillium method override handler
See crate-level docs for an explanation
*/
#[derive(Clone, Debug)]
pub struct MethodOverride {
param: &'static str,
allowed_methods: HashSet<Method>,
}
impl Default for MethodOverride {
fn default() -> Self {
Self {
param: "_method",
allowed_methods: HashSet::from_iter([Method::Put, Method::Patch, Method::Delete]),
}
}
}
impl MethodOverride {
/// constructs a new MethodOverride handler with default allowed methods and param name
pub fn new() -> Self {
Self::default()
}
/**
replace the default allowed methods with the provided list of methods
default: `put`, `patch`, `delete`
```
# use trillium_method_override::MethodOverride;
let handler = MethodOverride::new().with_allowed_methods(["put", "patch"]);
```
*/
pub fn with_allowed_methods<M>(mut self, methods: impl IntoIterator<Item = M>) -> Self
where
M: TryInto<Method>,
<M as TryInto<Method>>::Error: Debug,
{
self.allowed_methods = methods.into_iter().map(|m| m.try_into().unwrap()).collect();
self
}
/**
replace the default param name with the provided param name
default: `_method`
```
# use trillium_method_override::MethodOverride;
let handler = MethodOverride::new().with_param_name("_http_method");
```
*/
pub fn with_param_name(mut self, param_name: &'static str) -> Self {
self.param = param_name;
self
}
}
#[async_trait]
impl Handler for MethodOverride {
async fn run(&self, mut conn: Conn) -> Conn {
if conn.method() != Method::Post {
return conn;
}
let qs = conn_unwrap!(QueryStrong::parse(conn.querystring()).ok(), conn);
let method_str = conn_unwrap!(qs.get_str(self.param), conn);
let method: Method = conn_unwrap!(method_str.try_into().ok(), conn);
if self.allowed_methods.contains(&method) {
conn.inner_mut().set_method(method);
}
conn
}
}
/// Alias for MethodOverride::new()
pub fn method_override() -> MethodOverride {
MethodOverride::new()
}