Search code examples
rusterror-handlingactix-web

Struggling to understand error propagation using a Result<_, _>


I want to write a function that intercepts the HttpRequest of an actix-web route handler. In the function, I want to extract the Authorization header JWT and decode it. If it succeeds, I want to return the claims. If not, I want to send a 401 response and return an error:

fn extract_jwt(req: HttpRequest) -> Result<TokenData<Claims>, anyhow::Error> {
    // forgive me my unwraps
    let authorization_header = req
        .headers()
        .get("Authorization")
        .unwrap()
        .to_str()
        .unwrap();
    let token = authorization_header
        .split_whitespace()
        .collect::<Vec<&str>>()[1];

    let key = DecodingKey::from_secret(&[]);
    let mut validation = Validation::new(Algorithm::HS256);
    validation.insecure_disable_signature_validation();
    validation.set_audience(&["account"]);

    match decode::<Claims>(&token, &key, &validation) {
        Ok(claims) => Ok(claims),
        Err(e) => {
            HttpResponse::Unauthorized().finish();
            anyhow::Error("Could not decode JWT: {:?}", e) // error here
        }
    }
}

In my match arms, I return either the claims, which is the TokenData<Claims> of the Result, and Rust is happy with that. But for the Err arm, I get the error:

expected function, tuple struct or tuple variant, found struct `anyhow::Error`

Clearly I am not understanding how to propagate this error back to the caller. For example, in any given route, I want to extract the JWT with this function:

#[get("/some/route")]
async fn get_things(req: HttpRequest) {
  if let claims = extract_jwt(req) {
    // Handle the route logic here
    // If there is an error in extract_jwt, it should handle sending the response
    // as Unauthorized, so there is no need for an `else` statement here
  }
}

Despite pouring over the Rust docs about match statements, the Result enum, and propagating errors, I haven't found the right combination to set this up right. What am I missing here?


Solution

  • anyhow::Error is a struct with private fields, so you can't construct it directly. In other words anyhow::Error("Could not decode JWT: {:?}", e) is invalid code.

    Because you use a println like syntax it's likely you meant to use the macro anyhow::anyhow instead.

    That leaves another problem still, your function returns a Result that means any returned value has to be constructed with either Ok() or Err(). So you'll have to add that as well.

    TL;DR replace anyhow::Error("Could not decode JWT: {:?}", e) with Err(anyhow::anyhow!("Could not decode JWT: {:?}", e))