LoginSignup
14
1

More than 1 year has passed since last update.

Rustのlambda_http辛すぎなのでaxumしてみた

Last updated at Posted at 2022-07-10

目的

AWS LambdaでAPIっぽいことをするにはlambda_httpを使うことになるんですが、HTTPの低レイヤーを相手にすることが多くて辛いです。
何かいいものが無いかと調べていたら、数日前にaxum-aws-lambdaというものが出来ていたのでこれを使ってみました(この記事を書いた日からだと27日前でした)。

コード

lambda_httpだけでやってみる。

Cargo.toml
[package]
name = "web"
version = "0.1.0"
edition = "2021"

[dependencies]
lambda_http = "0.5.2"
serde_json = "1"
tokio = { version = "1", features = ["full"] }

main.rs
use lambda_http::{
    http::{response::Builder, Method, StatusCode},
    service_fn, Body, Error, Request, RequestExt, Response,
};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_http::run(service_fn(func)).await?;
    Ok(())
}

async fn func(request: Request) -> Result<Response<Body>, Error> {
    let unmatch = not_found();
    match request.uri().path() {
        "/name" => match request.method() {
            &Method::GET => execute_get(&request).await,
            &Method::POST => execute_post(&request).await,
            _ => unmatch,
        },
        _ => unmatch,
    }
}

fn not_found() -> Result<Response<Body>, Error> {
    let builder = Builder::new().status(StatusCode::NOT_FOUND);
    let json = json!({});
    Ok(builder.body(Body::Text(json.to_string()))?)
}

async fn execute_get(request: &Request) -> Result<Response<Body>, Error> {
    let params = request.query_string_parameters();
    let name = params.first("name").unwrap_or("World");
    let builder = Builder::new().status(StatusCode::OK);
    let json = json!({
        "type": "get",
        "message": format!("{} World!", name)
    });
    Ok(builder.body(Body::Text(json.to_string()))?)
}

async fn execute_post(request: &Request) -> Result<Response<Body>, Error> {
    let json: serde_json::Value = match request.body() {
        Body::Text(text) => serde_json::from_str(text).unwrap_or(serde_json::Value::Null),
        _ => serde_json::Value::Null,
    };
    let builder = Builder::new().status(StatusCode::OK);
    let json = json!({
        "type": "post",
        "message": format!("{} World!", json["name"].as_str().unwrap_or("World"))
    });
    Ok(builder.body(Body::Text(json.to_string()))?)
}

辛いところ

  • ルーティングするためにパスやメソッドを分析
  • not foundの準備
  • クエリーパラメーターを分解
  • リクエストBodyの解析
  • レスポンスを作る

aws-lambda-axumを使う

Cargo.toml
[package]
name = "web"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.5.11"
axum-aws-lambda = "0.1.0"
lambda_http = "0.5.2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
main.rs
use axum::{Router, routing::{get, post}, response::IntoResponse, Json, extract::Query};
use serde::Deserialize;
use serde_json::json;

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(execute_get))
        .route("/", post(execute_post))
    ;

    let app = lambda_http::tower::ServiceBuilder::new()
        .layer(axum_aws_lambda::LambdaLayer::default())
        .service(app);

    lambda_http::run(app).await.unwrap();
}

#[derive(Deserialize)]
pub struct GetParameter {
    name: String,
}

async fn execute_get(Query(params): Query<GetParameter>) -> impl IntoResponse {
    Json(json!({
        "type": "get",
        "message": format!("{} Worled!", params.name)
    }))
}

#[derive(Deserialize)]
pub struct PostParameter {
    name: String,
}

async fn execute_post(Json(params): Json<PostParameter>) -> impl IntoResponse {
    Json(json!({
        "type": "post",
        "message": format!("{} Worled!", params.name)
    }))
}

まとめ

辛いとこまとめて解決できました。
lambda_httpもaxumもtower-layerを利用いるので相性が良いようです。
axum-aws-lambdaのサンプルでは他のtower-layerも使っていて、CORSなどの機能を簡単に追加しています。
自分もtower-cookieを利用していますが問題無く使えています。

14
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
1