Как отправить данные на сервер AXUM с клиента через POST-запрос, используя метод fetch, и сохранить их в векторе в разделе <i>Shared mutable state</i>? Я использую тип String для хранения данных, как рекомендовано в документации _docs.rs/axum/latest/axum/extract/struct.State.html, но столкнулся с проблемой, когда изменил тип на вектор в структуре. Можете подсказать, как исправить код? Также интересуют сценарии с точки зрения производительности в случаях: когда вектор только читается и когда он читается и изменяется. Кроме того, будет ли обработка данных быстрее, если вектор будет хранить данные в виде &str вместо String?
Для переноса данных из приложения на Axum в вектор и их сохранения в состоянии, рекомендуется использовать `Arc<Mutex<Vec<String>>>` или похожую конструкцию. Давайте рассмотрим пример того, как это можно сделать. ### Шаг 1: Определение состояния Сначала вам нужно определить структуру состояния, которая будет хранить ваш вектор. ```rust use axum::{ extract::{Extension, Json}, response::IntoResponse, routing::post, Router, }; use std::{sync::{Arc, Mutex}, collections::VecDeque}; use serde_json::Value; #[derive(Debug)] struct AppState { data: Vec<String>, } type SharedState = Arc<Mutex<AppState>>; ``` ### Шаг 2: Обработка POST-запросов Далее, создайте обработчик для обработки входящих POST-запросов и извлечения данных. ```rust async fn add_data( Extension(state): Extension<SharedState>, Json(payload): Json<String>, ) -> impl IntoResponse { let mut state = state.lock().unwrap(); state.data.push(payload); (axum::http::StatusCode::OK, "Data added") } ``` ### Шаг 3: Настройка маршрутов Теперь создайте маршруты и начните сервер. ```rust #[tokio::main] async fn main() { let state = Arc::new(Mutex::new(AppState { data: Vec::new() })); let app = Router::new() .route("/add", post(add_data)) .layer(Extension(state)); axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); } ``` ### Шаг 4: Отправка данных с клиента Используйте метод `fetch` для отправки данных на сервер. Например, вы можете использовать следующий код на JavaScript: ```javascript async function sendData(data) { const response = await fetch('http://localhost:3000/add', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (!response.ok) { console.error('Error sending data:', response.statusText); } } ``` ### Производительность 1. **Чтение данных:** Если ваш вектор используется только для чтения (например, в обработчиках GET-запросов), использование `Arc<Mutex<...>>` будет приемлемым, так как только один поток будет получать блокировку для чтения. 2. **Запись и чтение данных:** Когда данные также изменяются, могут возникнуть проблемы с производительностью, так как `Mutex` будет блокировать доступ к вектору. Чтобы избежать этого, можно рассмотреть использование других подходов, таких как `RwLock`, который позволяет нескольким потокам одновременно читать данные. ### Хранение данных в виде `&str` или `String` Хранение данных в виде `&str` вместо `String` может привести к сложностям, так как `&str` является срезом строки и ему нужен запас (например, `String`), чтобы иметь возможность изменять содержимое. `String` позволяет вам динамически изменять или добавлять новые элементы. Если необходимо использовать тип `&str`, вам придется хранить его в некотором виде, например, в строках внутри `String`. В общем, для хранения изменений, которые могут происходить многопоточно, рекомендуется использовать `Arc<Mutex<Vec<String>>>`.
Кажется, ты просто в своих же типах запутался. Всё работает (ниже работающий код)
Подскажите, пожалуйста, как исправить код и есть ли разные решения с точки зрения скорости работы для случаев:
- такой вектор только читают
- вектор читают и изменяют.
Если только читают, то 1 раз его проинициализировал, а в стейте пусть лежит Arc без mutex.
Если и читают и пишут, то RwLock, но стоит подумать над структурами данных, тк у тебя ради добавления одной записи блокируется весь список. Что-нибудь на основе связных списков например позволит не блочить весь вектор.
use axum::extract::State; use axum::http::StatusCode; use axum::routing::{get, post}; use axum::{Json, Router}; use serde::{Deserialize, Serialize}; use std::sync::{Arc, Mutex}; #[derive(Debug, Clone)] struct AppState { d: Arc<Mutex<Vec<String>>>, } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let state = AppState { d: Arc::new(Mutex::new(vec![])), }; let app = Router::new() .route("/info", post(create_user)) .route("/list_users", get(list_users)) .with_state(state); let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); axum::serve(listener, app).await.unwrap(); Ok(()) } #[derive(Deserialize)] pub struct FromBrowser { username: String, } #[derive(Serialize)] pub struct User { username: String, } async fn create_user( State(state): State<AppState>, Json(payload): Json<FromBrowser>, ) -> (StatusCode, Json<User>) { // ... let user = User { username: payload.username, }; { let mut d = state.d.lock().expect("mutex was poisoned"); d.push(user.username.to_owned()); } println!("state = {:?}", state); (StatusCode::CREATED, Json(user)) } async fn list_users(State(state): State<AppState>) -> (StatusCode, Json<Vec<String>>) { let users = state.d.lock().expect("mutex was poisoned").clone(); (StatusCode::OK, Json(users)) }