0
votes

I have this below code, everything works expect the error_handler. I'm not sure whats wrong. If I make post request with correct format I get 200 with response data. But on bad request I'm expecting 400 with JSON data which is not receiving. It just says 400, but doesn't return any error json data.

Expected Behavior

BAD REQ -> curl -X POST -H "Content-Type: application/json" -d '{"bad": "Someone was here"}' localhost:8080/send

status 400
{
    error: String,
    server_id: usize,
    request_count: usize,
}

Current Behavior

BAD REQ -> curl -X POST -H "Content-Type: application/json" -d '{"bad": "Someone was here"}' localhost:8080/send

status 400

Possible Solution

I replaced .data with .app_data it works, but then it not able to find AppState

Code

pub struct MessageApp {
    pub port: u16,
}

impl MessageApp {
    pub fn new(port: u16) -> Self {
        Self{port}
    }

    pub async fn run(&self) -> std::io::Result<()> {
        let messages = Arc::new(Mutex::new(vec![]));
        println!("Starting http server: 127.0.0.1:{}", self.port);

        HttpServer::new(move || {
            App::new()
                .data(AppState {
                    server_id: SERVER_COUNTER.fetch_add(1, Ordering::SeqCst),
                    request_count: Cell::new(0),
                    messages: messages.clone(),
                })
                .wrap(middleware::Logger::new(LOG_FORMAT))
                .service(index)
                .service(
                    web::resource("/send")
                    .data(
                        web::JsonConfig::default()
                            .limit(4096)
                            .error_handler(post_error)
                    )
                    .route(web::post().to(post))
                )
                .service(clear)
        })
        .bind(("127.0.0.1", self.port))?
        .workers(8)
        .run()
        .await
    }
}

error handler

fn post_error(err: JsonPayloadError, req: &HttpRequest) -> Error {
    let extns = req.extensions();
    let state = extns.get::<web::Data<AppState>>().unwrap();
    let request_count = state.request_count.get() + 1;
    state.request_count.set(request_count);

  let post_error = PostError {
      server_id: state.server_id,
      request_count,
      error: format!("{}", err),
  };

  InternalError::from_response(err, HttpResponse::BadRequest().json(post_error)).into()
}

Environment

  • Pop OS 20.04 LTS
  • Rust Version: rustc 1.45.2 (d3fb005a3 2020-07-31)
  • Actix Web Version: 2.0.0
1

1 Answers

2
votes

As I mentioned before in possible solutions.

After taking help from author on github. He mentioned I need to move out .app_data to get rid of this issue or move on to new version 3.0 which resolves this issue.

But using version 2.0.0 below is the solution:

HttpServer::new(move || {
            App::new()
                .data(AppState {
                    server_id: SERVER_COUNTER.fetch_add(1, Ordering::SeqCst),
                    request_count: Cell::new(0),
                    messages: messages.clone(),
                })
                .wrap(middleware::Logger::new(LOG_FORMAT))
                .service(index)
                .service(
                    web::resource("/send")
                    .app_data(
                        web::JsonConfig::default().limit(4096).error_handler(post_error)
                    )
                    .route(web::post().to(post))
                )
                .service(clear)
        })
        .bind(("127.0.0.1", self.port))?
        .workers(8)
        .run()
        .await

As well the method for extracting AppState I was doing wrong. which fixed below inside post_error function.

fn post_error(err: JsonPayloadError, req: &HttpRequest) -> Error {
    // let extns = req.extensions();
    // let state = extns.get::<web::Data<AppState>>().unwrap();
    let state = req.app_data::<web::Data<AppState>>().unwrap();
    let request_count = state.request_count.get() + 1;
    state.request_count.set(request_count);

    let post_error = PostError {
        server_id: state.server_id,
        request_count,
        error: format!("{}", err),
    };

    InternalError::from_response(err, HttpResponse::BadRequest().json(post_error)).into()
}