ntex-rs/ntex

Help: How to return errors during serde serialization and deserialization

jing-951 opened this issue · 3 comments

I have implemented WebResponseError for serde errors, but I found that the return result is not what I expected. I always feel that ntex serializes JSON data during route parsing, not when using function parameters in the processor. When I want to know, how can I capture serialization errors and convert them into the results I want before returning them。

This is my incorrect implementation

use ntex::{http::{self, StatusCode}, web};
use serde::{Deserialize,Serialize};
use serde_json::json;


#[derive(Debug,Deserialize,Serialize)]
pub struct AppRes{
    code:u16,
    msg:&'static str,
}


#[derive(Debug)]
pub enum AppErr {
    InnError(u16,&'static str),
    SqlxError(u16),
    BadClientData(u16,&'static str),
    SerdeError,
    Timeout,
}

impl std::fmt::Display for AppErr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            AppErr::BadClientData(c, m) => write!(
                f, "BadClientData - error_code:{} - error_info:{}", c, m),
            AppErr::InnError(c, m) => write!(
                f,"InnError -  error_code:{} - error_info:{}",c,m
            ),
            AppErr::SqlxError(c) => write!(
                f,"sqlx query error - error_code:{} - error_info:sqlx error",c,
            ),
            AppErr::SerdeError => write!(
                f,"serde error"
            ),
            AppErr::Timeout => write!(f,"timeout")
        }
    }
}

impl web::error::WebResponseError for AppErr{
    fn error_response(&self, _: &web::HttpRequest) -> web::HttpResponse {
        match &self{
            AppErr::BadClientData(c,m ) | 
            AppErr::InnError(c, m) =>{
                web::HttpResponse::build(self.status_code())
                .set_header("content-type", "application/json; charset=utf-8")
                .body(json!(AppRes{code:*c,msg:m}))
            },
            AppErr::Timeout => {
                web::HttpResponse::build(self.status_code())
                .set_header("content-type", "text/html; charset=utf-8")
                .body("timeout")
            },
            AppErr::SqlxError(c) => {
                web::HttpResponse::build(self.status_code())
                .set_header("content-type", "application/json; charset=utf-8")
                .body(json!(AppRes{code:*c,msg:"server error"}))
            },
            AppErr::SerdeError => {
                web::HttpResponse::build(self.status_code())
                .set_header("content-type", "application/json; charset=utf-8")
                .body(json!(AppRes{code:10000,msg:"bad parameter"}))
            }
        }
        
    }
    fn status_code(&self) -> http::StatusCode {
        match &self{
            AppErr::BadClientData(_, _) => StatusCode::BAD_REQUEST,
            AppErr::InnError(_, _) => StatusCode::INTERNAL_SERVER_ERROR,
            AppErr::Timeout => StatusCode::REQUEST_TIMEOUT,
            AppErr::SqlxError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppErr::SerdeError => StatusCode::BAD_REQUEST,
        }
    }
}

error handling depends on where you use it. WebResponseError is used if you return result from your handle function. could you add usage example?

web::error::WebResponseError

Thank you for your reply. I don't know where to capture deserialization errors. I thought I had implemented web:: error:: Webex ResponseError to capture the errors I wanted in the processor. Looking forward to your reply.
Here is my test code:

use ntex::web;
use ntex::web::middleware::Logger;
use ntex::web::types::Json;
use serde::Deserialize;

mod apps;
use crate::apps::apperr::AppErr;

#[ntex::main]
async fn main()->Result<(), std::io::Error>  {
    std::env::set_var("RUST_LOG", "info");
    env_logger::init();
    
    web::HttpServer::new(
        ||{
            web::App::new()
            .wrap(Logger::default())
            .service(
                web::scope("/").route("/", web::post().to(index))
            )
        }
    )
    .bind("127.0.0.1:3000")?
    .run()
    .await
}



#[derive(Debug,Deserialize)]
struct UserInfo{
    name:String,
    pwd:String,
}

async fn index(info:Json<UserInfo>)->Result<String,AppErr>{
    println!("name:{},password:{}",info.name,info.pwd);
    Ok(format!("Hi {}",info.name))
}