0Pricing
Learn Rust Coding · レッスン

エンドポイントとモデル

ルートとデータ

「エンドポイントとモデル」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

ルートとデータモデル

APIは、エンドポイント(URLとHTTPメソッドの組み合わせ)と、そこを流れるモデル(データの形)によって定義されます。このレッスンでは、serdeを使ってリクエストとレスポンスのモデルを定義し、AxumでCRUD形式のルートを接続します。

モデルを定義する

モデルは単純なRust構造体です。レスポンスでJSONに変換できるようにSerializeを導出し、リクエストボディから解析できるようにDeserializeを導出します。フィールド名はそのままJSONキーに対応します。

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Clone)]
struct Todo {
    id: u32,
    title: String,
    done: bool,
}

リクエスト型とレスポンス型を分ける

リソースを作成するとき、クライアントはサーバーが割り当てるidを送信すべきではありません。リクエストボディには入力用の別の構造体を使い、レスポンスには完全なモデルを使います。これにより契約が明確になり、クライアントが設定すべきでないフィールドを設定するのを防げます。

use serde::Deserialize;

#[derive(Deserialize)]
struct CreateTodo {
    title: String,
}
// The handler assigns the id and sets done = false.

GET:リソースを一覧表示する

GET /todosハンドラーは、コレクション全体をJSON配列として返します。共有状態を読み取り、ロックの外へデータをすばやくクローンして取り出し、Jsonでラップします。

use axum::{extract::State, Json};
use std::sync::{Arc, Mutex};

type Store = Arc<Mutex<Vec<Todo>>>;
#[derive(Clone, serde::Serialize)]
struct Todo { id: u32, title: String, done: bool }

async fn list_todos(State(store): State<Store>) -> Json<Vec<Todo>> {
    let todos = store.lock().unwrap().clone();
    Json(todos)
}

POST:リソースを作成する

POST /todosハンドラーは、JsonエクストラクターでJSONボディを読み取り、新しいIDを割り当て、項目を保存し、ステータス201 Createdとともに返します。タプル(StatusCode, Json<T>)を使うと、ステータスとボディの両方を設定できます。

use axum::{extract::State, Json, http::StatusCode};

async fn create_todo(
    State(store): State<Store>,
    Json(input): Json<CreateTodo>,
) -> (StatusCode, Json<Todo>) {
    let mut todos = store.lock().unwrap();
    let id = todos.len() as u32 + 1;
    let todo = Todo { id, title: input.title, done: false };
    todos.push(todo.clone());
    (StatusCode::CREATED, Json(todo))
}

パスパラメーター

1つのリソースを取得するには、パスパラメーターを使ってURLの一部を取得します。ルートで/todos/{id}のように宣言し、Pathエクストラクターで取り出します。要求した型(ここではu32)への変換は自動的に行われます。

use axum::extract::{Path, State};
use axum::{Json, http::StatusCode};

async fn get_todo(
    State(store): State<Store>,
    Path(id): Path<u32>,
) -> Result<Json<Todo>, StatusCode> {
    let todos = store.lock().unwrap();
    match todos.iter().find(|t| t.id == id) {
        Some(t) => Ok(Json(t.clone())),
        None => Err(StatusCode::NOT_FOUND),
    }
}

クエリパラメーター

フィルタリングやページネーションには、/todos?done=trueのようなクエリ文字列を使用します。Queryエクストラクターで取得し、Deserializeを導出した構造体に格納します。省略可能なフィールドにはOptionを使うため、パラメーターがなくても問題ありません。

use axum::extract::{Query, State};
use axum::Json;
use serde::Deserialize;

#[derive(Deserialize)]
struct Filter { done: Option<bool> }

async fn filtered(
    State(store): State<Store>,
    Query(f): Query<Filter>,
) -> Json<Vec<Todo>> {
    let todos = store.lock().unwrap();
    let out = todos.iter()
        .filter(|t| f.done.map_or(true, |d| t.done == d))
        .cloned().collect();
    Json(out)
}

PUTとDELETE

更新にはボディ付きのPUT /todos/{id}を、削除にはDELETE /todos/{id}を使用します。どちらもIDで項目を検索し、存在しない場合は404を返します。削除が成功した場合、通常は204 No Contentを返します。

use axum::extract::{Path, State};
use axum::http::StatusCode;

async fn delete_todo(
    State(store): State<Store>,
    Path(id): Path<u32>,
) -> StatusCode {
    let mut todos = store.lock().unwrap();
    let before = todos.len();
    todos.retain(|t| t.id != id);
    if todos.len() < before { StatusCode::NO_CONTENT }
    else { StatusCode::NOT_FOUND }
}

ルートをまとめて接続する

すべてのハンドラーをルーターに登録します。同じパスのメソッドをまとめ、/todosでは一覧表示と作成を、/todos/{id}では取得、更新、削除を処理します。.with_stateで共有ストアを接続します。

use axum::{routing::get, Router};

fn build(store: Store) -> Router {
    Router::new()
        .route("/todos", get(list_todos).post(create_todo))
        .route("/todos/{id}",
            get(get_todo).delete(delete_todo))
        .with_state(store)
}

入力を検証する

クライアントからのデータを決してそのまま信用してはいけません。ハンドラー内で検証し、無効な場合は400 Bad Requestを返します。ここでは保存する前に空のタイトルを拒否し、不正なデータがシステムに入らないようにします。

use axum::{Json, extract::State, http::StatusCode};

async fn create_validated(
    State(store): State<Store>,
    Json(input): Json<CreateTodo>,
) -> Result<(StatusCode, Json<Todo>), StatusCode> {
    if input.title.trim().is_empty() {
        return Err(StatusCode::BAD_REQUEST);
    }
    let mut todos = store.lock().unwrap();
    let id = todos.len() as u32 + 1;
    let todo = Todo { id, title: input.title, done: false };
    todos.push(todo.clone());
    Ok((StatusCode::CREATED, Json(todo)))
}

一貫したエラーレスポンス

ステータスコードだけを返しても動作しますが、本番環境のAPIでは通常、JSON形式のエラーボディも返します。一般的な方法は、IntoResponseを実装するカスタムエラー列挙型を作り、各バリアントをステータスとメッセージに対応付けることです。これにより、クライアントは予測可能で機械可読なエラーを受け取れます。

use axum::response::{IntoResponse, Response};
use axum::http::StatusCode;
use axum::Json;
use serde_json::json;

enum ApiError { NotFound, BadRequest(String) }

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, msg) = match self {
            ApiError::NotFound => (StatusCode::NOT_FOUND, "not found".to_string()),
            ApiError::BadRequest(m) => (StatusCode::BAD_REQUEST, m),
        };
        (status, Json(json!({ "error": msg }))).into_response()
    }
}

理解度チェック

エンドポイントとモデルについての理解度を確認しましょう。

まとめ

エンドポイントとモデルを定義しました。

  • モデルはSerialize/Deserializeを導出した構造体です。入力には別の型を使います。
  • Json、Path、Query、Stateでリクエストデータを取り出します。
  • CRUD操作をGET/POST/PUT/DELETEに対応付け、適切なステータスコードを使用します。
  • 入力を検証し、不正なデータには400を返します。
  • IntoResponseを実装したカスタムエラー型により、一貫したJSONエラーを返せます。

よくある質問

「エンドポイントとモデル」レッスンは無料ですか?

はい。「エンドポイントとモデル」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「エンドポイントとモデル」で何を学びますか?

ルートとデータ ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「エンドポイントとモデル」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. プロジェクトのセットアップ
  2. エンドポイントとモデル
  3. データベース統合
  4. APIのテスト
← Learn Rust Codingに戻る