actix_web_lab/
acceptable.rs

1#![allow(deprecated)]
2
3use actix_web::{
4    guard::{Guard, GuardContext},
5    http::header::Accept,
6};
7
8/// A guard that verifies that an `Accept` header is present and it contains a compatible MIME type.
9///
10/// An exception is that matching `*/*` must be explicitly enabled because most browsers send this
11/// as part of their `Accept` header for almost every request.
12///
13/// # Examples
14/// ```
15/// use actix_web::{web, HttpResponse};
16/// use actix_web_lab::guard::Acceptable;
17///
18/// web::resource("/images")
19///     .guard(Acceptable::new(mime::IMAGE_STAR))
20///     .default_service(web::to(|| async {
21///         HttpResponse::Ok().body("only called when images responses are acceptable")
22///     }));
23/// ```
24#[deprecated(since = "0.19.0", note = "Type has graduated to Actix Web.")]
25#[derive(Debug, Clone)]
26pub struct Acceptable {
27    mime: mime::Mime,
28
29    /// Wether to match `*/*` mime type.
30    ///
31    /// Defaults to false because it's not very useful otherwise.
32    match_star_star: bool,
33}
34
35impl Acceptable {
36    /// Constructs new `Acceptable` guard with the given `mime` type/pattern.
37    pub fn new(mime: mime::Mime) -> Self {
38        Self {
39            mime,
40            match_star_star: false,
41        }
42    }
43
44    /// Allows `*/*` in the `Accept` header to pass the guard check.
45    pub fn match_star_star(mut self) -> Self {
46        self.match_star_star = true;
47        self
48    }
49}
50
51impl Guard for Acceptable {
52    fn check(&self, ctx: &GuardContext<'_>) -> bool {
53        let accept = match ctx.header::<Accept>() {
54            Some(hdr) => hdr,
55            None => return false,
56        };
57
58        let target_type = self.mime.type_();
59        let target_subtype = self.mime.subtype();
60
61        for mime in accept.0.into_iter().map(|q| q.item) {
62            return match (mime.type_(), mime.subtype()) {
63                (typ, subtype) if typ == target_type && subtype == target_subtype => true,
64                (typ, mime::STAR) if typ == target_type => true,
65                (mime::STAR, mime::STAR) if self.match_star_star => true,
66                _ => continue,
67            };
68        }
69
70        false
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use actix_web::{http::header, test::TestRequest};
77
78    use super::*;
79
80    #[test]
81    fn test_acceptable() {
82        let req = TestRequest::default().to_srv_request();
83        assert!(!Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
84
85        let req = TestRequest::default()
86            .insert_header((header::ACCEPT, "application/json"))
87            .to_srv_request();
88        assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
89
90        let req = TestRequest::default()
91            .insert_header((header::ACCEPT, "text/html, application/json"))
92            .to_srv_request();
93        assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
94    }
95
96    #[test]
97    fn test_acceptable_star() {
98        let req = TestRequest::default()
99            .insert_header((header::ACCEPT, "text/html, */*;q=0.8"))
100            .to_srv_request();
101
102        assert!(Acceptable::new(mime::APPLICATION_JSON)
103            .match_star_star()
104            .check(&req.guard_ctx()));
105    }
106}