Search code examples
reactjsrustpreflightrust-rocket

Error when sending POST request from React app to Rocket backend returns failure


I'm writing a simple web with Rocket as backend and React as frontend.

The code snippet looks like this for login page

#[post("/login", data = "<data>")]
pub fn login(
    conn: DbConn,
    mut cookies: Cookies<'_>,
    data: Form<LoginForm>,
) -> Result<JsonValue, NotFound<String>> {
    let valid_account = match Account::find_by_email(&*conn, data.email.as_str()) {
        Ok(account) => {
            if account.password == data.password {
                account
            } else {
                return Err(NotFound("Incorrect email or password!".to_string()));
            }
        }
        Err(_) => return Err(NotFound("Incorrect email or password!".to_string())),
    };

    cookies.add_private(
        Cookie::build(AUTH_COOKIE, valid_account.id.to_string())
            .same_site(rocket::http::SameSite::Strict)
            .finish(),
    );
    Ok(json!({
        "email": valid_account.email,
        "name": valid_account.name,
    }))
}

Code for main.rs

fn main() {
    rocket::ignite()
        .mount("/", routes![
                account::login::login,
            ],
        )
        .register(catchers![errors::unauthorized])
        .attach(rocket_cors::CorsOptions::default().to_cors().unwrap())
        .manage(establish_connection())
        .launch();
}

and code for React when trying to send the post request

export const postForm = async (
  pathUrl: string,
  postInfo: { [name: string]: any }
) => {
  let axiosConfig = {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Access-Control-Allow-Origin': '*',
    },
  };
  try {
    const response = await axios.post(
      baseUrl + pathUrl,
      querystringify.stringify(postInfo),
      axiosConfig
    );
    return response.data as CurrentUser;
  } catch (err) {
    console.log(err);
    return Promise.reject(err.response);
  }
};

The code works fine it I enter the correct email and password. However, it cannot capture the error message if I enter the wrong credentials. Rocket log are the same between successful login and failure login which are

OPTIONS /login:
    => Error: No matching routes for OPTIONS /login.
    => Warning: Responding with 404 Not Found catcher.
    => CORS Fairing: Turned missing route OPTIONS /login into an OPTIONS pre-flight request
    => Response succeeded.
POST /login application/x-www-form-urlencoded:
    => Matched: POST /login (login)
    => Outcome: Success
    => Response succeeded.

and the error log in browser I captured was Error: "Request failed with status code 404" which was not the expected error message hard coded inside post function.

I believe it has something to do with Option or preflight processed inside Rocket which maybe in the purpose of security. But how can I suppress the system error and let my code to take over?

I have read previous SO post like state undefined: while sending post request from react app and GitHub issues like https://github.com/SergioBenitez/Rocket/issues/25. And still cannot find answer for my problem.

Thanks in advance!


Solution

  • Apparently I made several mistakes here due to unfamiliar with Rocket and React.
    List here in case someone made the similar mistakes.

    The 404 status code is from the first code snippets Result<JsonValue, NotFound<String>>. So if we write the return type as Result<JsonValue, Unauthorized<String>>, it would return 401 as unauthorized user.

    Second, axios only receives json type and cannot parse string (correct me if I'm wrong). So we need to change the return type in server to Result<JsonValue, Unauthorized<JsonValue>>.