Как отправить данные на сервер 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>>>`.
Кажется, ты просто в своих же типах запутался. Всё работает (ниже работающий код) <br/> <br/> <blockquote>Подскажите, пожалуйста, как исправить код и есть ли разные решения с точки зрения скорости работы для случаев:<br/>
- такой вектор только читают<br/>
- вектор читают и изменяют.</blockquote> <br/> Если только читают, то 1 раз его проинициализировал, а в стейте пусть лежит Arc без mutex. <br/> Если и читают и пишут, то RwLock, но стоит подумать над структурами данных, тк у тебя ради добавления одной записи блокируется весь список. Что-нибудь на основе связных списков например позволит не блочить весь вектор. <br/> <br/> <pre><code class="rust">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))
}</code></pre>