🫥
rust axum framework cook book1
Handler
- text/plainで 固定の文字列を返したい
- status codeを変えたい
- response ヘッダを追加したい
- jsonでpostされた内容をstructをbindしたい
- jsonとstructが完全に合わなかった場合文字でエラーがでるが、これをカスタマイズしたい()
text/plainで 固定の文字列を返したい
async fn handler() -> &'static str {
"Hello, world!"
}
固定のhtmlファイルを返したい
async fn handler() -> Html<&'static str> {
Html(include_str!("../views/index.html"))
}
status codeを変えたい / response headerを変えたい/追加したい
impl IntoResponseで返すことで、細かく制御できます
CustomErrorの構造体定義は省略
async fn status400(params: Json<Value>) -> impl IntoResponse {
let builder = Response::builder();
let body = Json(json!(CustomError {
message: "app_name is required".to_string()
}));
builder
.status(StatusCode::BAD_REQUEST)
.header("Content-type", "application/json")
.body(body)
.unwrap()
.into_body()
}
JSONを受け取りstructにバインドしたい
pub async fn test4(Json(params): Json<HomeResponse>) -> Result<Json<HomeResponse>> {
format::json(params)
}
Formを受け取りstructにバインドしたい
レスポンスはjsonで返します
pub async fn test4(Form(params): Json<HomeResponse>) -> Result<Json<HomeResponse>> {
format::json(params)
}
自動でstructを受け取るとエラーメッセージが固定なのでカスタマイズしたい
Optionでないフィールドがpostされたjsonに含まれていなかったり、型が違った場合、
Failed to deserialize the JSON body into the target type: app_name: invalid type: integer
10
, expected a string at line 1 column 14
などのメッセージが返されてしまいます。
これをカスタマイズするには
より汎用的な型で受け取り、自分でstructに紐づければ良いです。
fn create_response(status: StatusCode, value: serde_json::Value) -> impl IntoResponse {
let builder = Response::builder();
let body = serde_json::to_string(&value).unwrap();
builder
.status(status)
.header("Content-type", "application/json")
.body(body)
.unwrap()
}
async fn current3(params: Json<Value>) -> impl IntoResponse {
let value = serde_json::from_value::<HomeResponse>(params.0);
if &value.is_ok() == &true {
let v = json!(value.unwrap());
create_response(StatusCode::OK, v)
} else {
create_response(
StatusCode::BAD_REQUEST,
json!(CustomError {
message: value.err().unwrap().to_string()
}),
)
}
}
この方法であれば、自分でカスタマイズしたエラーを返すことができます.
他にも FromRequest traitを実装しておく方法もあります。
#[async_trait]
impl<B> FromRequest<B> for HomeResponse {
type Rejection = Response<Body>;
async fn from_request(req: Request, state: &B) -> std::result::Result<Self, Self::Rejection> {
let app_name = req
.headers()
.get("app_name")
.and_then(|value| value.to_str().ok())
.map(|value| value.to_string());
let error = CustomError {
message: "app_name is required".to_string(),
};
Err(format::json(error).into_response())
}
}
JsonRejectionを使う方法もありそうです。
Discussion