0Pricing
Learn Rust Coding · Lekcja

Endpointy i modele

Trasy i dane

Endpointy i modele to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.

Trasy i modele danych

Interfejs API definiują jego endpointy (adresy URL wraz z metodami HTTP) oraz modele (kształty danych) przepływające przez te endpointy. W tej lekcji zdefiniujesz modele żądań i odpowiedzi za pomocą serde oraz skonfigurujesz w Axum trasy w stylu CRUD.

Definiowanie modelu

Model to zwykła struktura Rust. Wyprowadź implementację Serialize, aby można było tworzyć z niej JSON w odpowiedziach, oraz Deserialize, aby można było analizować treść żądań. Nazwy pól są bezpośrednio mapowane na klucze JSON.

use serde::{Serialize, Deserialize};

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

Oddzielne typy żądań i odpowiedzi

Klienci nie powinni wysyłać przypisanego przez serwer pola id podczas tworzenia zasobu. Użyj osobnej struktury wejściowej dla treści żądania, a pełnego modelu dla odpowiedzi. Dzięki temu kontrakt jest jasny, a klienci nie mogą ustawiać pól, których nie powinni modyfikować.

use serde::Deserialize;

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

GET: wyświetlanie zasobów

Handler GET /todos zwraca całą kolekcję jako tablicę JSON. Odczytuje współdzielony stan, szybko kopiuje dane poza blokadę i opakowuje je w 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: tworzenie zasobu

Handler POST /todos odczytuje treść JSON za pomocą ekstraktora Json, przypisuje nowe id, zapisuje element i zwraca go ze statusem 201 Created. Krotka (StatusCode, Json<T>) pozwala ustawić zarówno status, jak i treść odpowiedzi.

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))
}

Parametry ścieżki

Aby pobrać pojedynczy zasób, przechwyć część adresu URL za pomocą parametru ścieżki. Zadeklaruj go w trasie jako /todos/{id} i wyodrębnij za pomocą ekstraktora Path. Żądany typ (tutaj u32) jest analizowany automatycznie.

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),
    }
}

Parametry zapytania

Filtrowanie i stronicowanie korzystają z query stringa, na przykład /todos?done=true. Przechwyć go za pomocą ekstraktora Query do struktury implementującej Deserialize. Dla opcjonalnych pól używaj Option, aby brakujące parametry nie powodowały problemów.

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 i DELETE

Aktualizowanie używa PUT /todos/{id} z treścią żądania, a usuwanie — DELETE /todos/{id}. Obie operacje wyszukują element po id i zwracają 404, jeśli go nie ma. Usuwanie zazwyczaj zwraca po powodzeniu 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 }
}

Łączenie tras

Zarejestruj każdy handler w routerze. Grupuj metody korzystające ze wspólnych ścieżek: /todos obsługuje listowanie i tworzenie, a /todos/{id} — pobieranie, aktualizowanie i usuwanie. Dołącz współdzielony magazyn za pomocą .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)
}

Weryfikowanie danych wejściowych

Nigdy nie ufaj danym od klienta. Sprawdzaj je wewnątrz handlera i zwracaj 400 Bad Request, gdy są nieprawidłowe. W tym przypadku odrzucamy pusty tytuł przed zapisaniem czegokolwiek, dzięki czemu błędne dane nie trafiają do systemu.

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)))
}

Spójne odpowiedzi błędów

Zwracanie samych kodów statusu działa, ale produkcyjne interfejsy API zwracają również treść błędu w formacie JSON. Często stosuje się niestandardowy enum błędów implementujący IntoResponse, który mapuje każdy wariant na status i komunikat. Dzięki temu klienci otrzymują przewidywalne błędy możliwe do odczytania przez maszyny.

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()
    }
}

Szybkie sprawdzenie

Sprawdź swoją wiedzę o endpointach i modelach.

Podsumowanie

Zdefiniowano endpointy i modele:

  • Modele są strukturami z wyprowadzonymi implementacjami Serialize/Deserialize; używaj osobnych typów wejściowych.
  • Json, Path, Query i State wyodrębniają dane z żądań.
  • Mapuj operacje CRUD na GET/POST/PUT/DELETE, używając odpowiednich kodów statusu.
  • Weryfikuj dane wejściowe i zwracaj 400 dla nieprawidłowych danych.
  • Niestandardowy typ błędu implementujący IntoResponse zapewnia spójne błędy JSON.

Często zadawane pytania

Czy lekcja „Endpointy i modele” jest bezpłatna?

Tak — pełny tekst „Endpointy i modele” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.

Co nauczysz się w „Endpointy i modele”?

Trasy i dane Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?

Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Endpointy i modele”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?

Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Konfiguracja projektu
  2. Endpointy i modele
  3. Integracja z bazą danych
  4. Testowanie API
← Powrót do Learn Rust Coding