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,QueryiStatewyodrę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
400dla nieprawidłowych danych. - Niestandardowy typ błędu implementujący
IntoResponsezapewnia 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
- Konfiguracja projektu
- Endpointy i modele
- Integracja z bazą danych
- Testowanie API