16

Is this is the only possibility to get the content-type header from an Actix-Web request? This has to check if the header is available or if to_str failed...

let req: actix_web::HttpRequest;

let content_type: &str = req
    .request()
    .headers()
    .get(actix_web::http::header::CONTENT_TYPE)
    .unwrap()
    .to_str()
    .unwrap();
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Markus
  • 512
  • 1
  • 4
  • 21
  • str != String ! – Stargateur Oct 21 '18 at 20:25
  • 1
    What should this tell me? I know that str is different from String... the first unwrap doesn't return a String, this is a HeaderValue, if this would be the idea... or because of the title? I've put in a "string value". ;-) – Markus Oct 22 '18 at 10:25
  • I guess you could replace `.unwrap().to_str()` with `.and_then(HeaderValue::to_str)` to have one less panic risk. – Tarmil Oct 24 '18 at 15:59

2 Answers2

18

Yes, that is the "only" possibility, but it's like that because:

  1. The header may not exist, headers().get(key) returns Option.
  2. The header may have non-ASCII chars, and HeaderValue::to_str might fail.

actix-web lets you handle those errors individually.

To simplify, you could make a helper function that does not differentiate between the two errors:

fn get_content_type<'a>(req: &'a HttpRequest) -> Option<&'a str> {
    req.headers().get("content-type")?.to_str().ok()
}

Full example:

use actix_web::{web, App, HttpRequest, HttpServer, Responder};

fn main() {
    HttpServer::new(|| App::new().route("/", web::to(handler)))
        .bind("127.0.0.1:8000")
        .expect("Cannot bind to port 8000")
        .run()
        .expect("Unable to run server");
}

fn handler(req: HttpRequest) -> impl Responder {
    if let Some(content_type) = get_content_type(&req) {
        format!("Got content-type = '{}'", content_type)
    } else {
        "No content-type header.".to_owned()
    }
}

fn get_content_type<'a>(req: &'a HttpRequest) -> Option<&'a str> {
    req.headers().get("content-type")?.to_str().ok()
}

Which will give you the results:

$ curl localhost:8000
No content-type header.⏎
$ curl localhost:8000 -H 'content-type: application/json'
Got content-type = 'application/json'⏎
$ curl localhost:8000 -H 'content-type: '
No content-type header.⏎

By the way, you may be interested in guards:

web::route()
    .guard(guard::Get())
    .guard(guard::Header("content-type", "text/plain"))
    .to(handler)
arve0
  • 3,424
  • 26
  • 33
  • Ok. I've expected that a webframework provides something like your `fn get_content_type` and not everybody needs to reinvent it. But thanks. – Markus Mar 12 '20 at 00:42
  • 1
    @Markus Depends on your perspective. actix-web gives you an API to the HTTP-protocol, with all the things that might fail. In this lower level context, getting the content type is straight forward. If you only want to work at a higher level, you can copy this code and publish it to crates.io – arve0 Mar 12 '20 at 13:14
3

I use the following from a route:

#[get("/auth/login")]
async fn login(request: HttpRequest, session: Session) -> Result<HttpResponse, ApiError> {
    let req_headers = request.headers();

    let basic_auth_header = req_headers.get("Authorization");
    let basic_auth: &str = basic_auth_header.unwrap().to_str().unwrap();
    // Keep in mind that calling "unwrap" here could make your application
    // panic. The right approach at this point is to evaluate the resulting
    // enum variant from `get`'s call

    println!("{}", basic_auth); // At this point I have the value of the header as a string

    // ...
}
Esteban Borai
  • 2,311
  • 1
  • 21
  • 27
  • 1
    Forgive my newbie question but what does Actix do/return when your unwrap fails, for example if you have an emoji in the header value? – David Bell Feb 11 '21 at 19:55
  • No problem, all questions are valid questions! When you call `unwrap` on any value, Rust your thread will panic if the resulting variant is either `Err` or `None` (based on the type returned), and application exits. Using `unwrap` most of the time is not recommended, the right approach is to handle the variant resulting from the `get` call. – Esteban Borai Feb 12 '21 at 20:11
  • 1
    Thank you, it works! Note that `request` and `session` on the example are injected by the framework automagically. – Kazuya Gosho Jul 13 '22 at 14:46
  • @DavidBell the handler crashes (panics), but the application does not exit as Esteban says, and the client will not get a proper response, the actix server will continue just fine though. – Kjell Andreassen Feb 01 '23 at 02:29