Théorie poem 2 exemple modification contexte
main et use en global
Dans le main on définie nos deux routes, une pour créer des messages, une autre pour en récupérer un :
use std::sync::{Arc, RwLock};
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
use poem::{EndpointExt, get, post};
let context = Arc::new(ExampleCtx::new());
let routes = poem::Route::new()
.at("/msg", post(hdl::insert_message))
.at("/msg/:id", get(hdl::get_message))
.data(context);
poem::Server::new(poem::listener::TcpListener::bind("0.0.0.0:3000"))
.run(routes)
.await
}
Contexte et son implémentation
Le contexte utilise un RwLock pour nous permettre de lire et modifier le tableau de messages depuis plusieurs fils d’exécution différents.
struct ExampleCtx {
messages: RwLock<Vec<model::Message>>,
}
impl ExampleCtx {
fn new() -> Self {
Self {
messages: RwLock::new(vec![]),
}
}
fn insert_message(&self, source: model::MessageInputDto) -> Option<usize> {
let mut messages = self.messages.write().ok()?;
messages.push(source.into());
Some(messages.len() - 1)
}
fn get_message(&self, id: usize) -> Option<model::Message> {
let messages = self.messages.read().ok()?;
messages.get(id).cloned()
}
}
Handlers de requête
C’est eux qui sont appelés lors d’une requête sur une route qui les concernent. Ils ont besoin du modèle et du contexte.
mod hdl {
use crate::ExampleCtx;
use crate::model;
use poem::web::{Data, Json, Path};
use std::sync::Arc;
#[poem::handler]
pub fn insert_message(
Json(message): Json<model::MessageInputDto>,
Data(context): Data<&Arc<ExampleCtx>>,
) -> Json<model::MessageOutputDto> {
let id = context.insert_message(message).unwrap();
let msg = context.get_message(id).unwrap();
Json(model::MessageOutputDto {
text: msg.text,
created_on: msg.created_on,
id,
})
}
#[poem::handler]
pub fn get_message(
Path(id): Path<usize>,
Data(context): Data<&Arc<ExampleCtx>>,
) -> Json<model::MessageOutputDto> {
let msg = context.get_message(id).unwrap();
Json(model::MessageOutputDto {
text: msg.text,
created_on: msg.created_on,
id,
})
}
}
Modèle
C’est ici que l’on défini ce qu’est un message stocké, en transit entrant, ou en transit sortant.
mod model {
#[derive(serde::Deserialize)]
pub struct MessageInputDto {
pub text: String,
}
#[derive(Clone)]
pub struct Message {
pub text: String,
pub created_on: chrono::DateTime<chrono::Local>,
}
impl std::convert::From<MessageInputDto> for Message {
fn from(value: MessageInputDto) -> Self {
Self {
text: value.text,
created_on: chrono::Local::now(),
}
}
}
#[derive(serde::Serialize)]
pub struct MessageOutputDto {
pub id: usize,
pub text: String,
pub created_on: chrono::DateTime<chrono::Local>,
}
}