Как отправить данные на сервер 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)) }