4 подписчика

Backend-driven UI в Rust

t.me/oneRustnoqRust Ежедневная рассылка Для чего нужна данная статья?

t.me/oneRustnoqRust Ежедневная рассылка

Для чего нужна данная статья? :

Создать Backend-driven UI - подход к построению пользовательского интерфейса, при котором логика и отображение контента контролируются сервером (backend).

Зачем Вам это уметь? :

Найти компромиссы между фреймворками и WASM (WebAssembly).

___________________________________________________________________________________

Варианты использования backend-driven UI:

Rocket и Темплейты:

#[macro_use]
extern crate rocket;
use rocket::response::content::Html;
#[get("/")]
fn index() -> Html<String> {
let data_from_server = fetch_data_from_backend(); // данные с бэкенда
let rendered_html = render_html_template(data_from_server); // HTML
Html(rendered_html)
}
fn fetch_data_from_backend() -> String {
// Логика получения данных с бэкенда
"Hello, from the backend!".to_string()
}
fn render_html_template(data: String) -> String {
// Логика генерации HTML на основе данных
format!("<html><body>{}</body></html>", data)
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
}

Actix и API:

use actix_web::{web, App, HttpServer, HttpResponse, Responder};
async fn index() -> impl Responder {
let data_from_server = fetch_data_from_backend().await; //данные
HttpResponse::Ok().body(data_from_server)
}
async fn fetch_data_from_backend() -> String {
// Логика получения данных с бэкенда
"Hello, from the backend!".to_string()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(web::resource("/").to(index))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

Warp и WebSocket для Real-time обновлений:

use warp::Filter;
#[tokio::main]
async fn main() {
let ws_route = warp::path!("ws")
.and(warp::ws())
.map(|ws: warp::ws::Ws| {
ws.on_upgrade(|websocket| handle_websocket(websocket));
});
warp::serve(ws_route).run(([127, 0, 0, 1], 3030)).await;
}
async fn handle_websocket(ws: warp::ws::WebSocket) {
// Логика обработки WebSocket-соединения
}

____________________________________________________________________________

Пример демонстрирует, как можно объединить backend-driven UI с использованием Rocket и темплейтов, Actix и API, Warp и WebSocket для real-time обновлений, а также WASM для передачи логики на сторону клиента. WASM может быть интегрирован в фронтенд, с использованием JavaScript и библиотеки wasm-bindgen.:

// Cargo.toml
[dependencies]
rocket = "0.5"
rocket_contrib = "0.5"
actix-web = "4"
warp = "0.3"
// main.rs
#[macro_use]
extern crate rocket;
use rocket_contrib::templates::Template;
use std::collections::HashMap;
use actix_web::{web, App, HttpServer, HttpResponse, Responder, HttpRequest};
use warp::Filter;
// Rocket and Templates
#[get("/")]
fn index() -> Template {
let mut context = HashMap::new();
context.insert("message", "Hello, from the backend!");
Template::render("index", &context)
}
// Actix and API
#[derive(Debug, Serialize)]
struct ApiResponse {
message: String,
}
async fn api_handler() -> impl Responder {
let data_from_server = fetch_data_from_backend().await;
HttpResponse::Ok().json(ApiResponse { message: data_from_server })
}
async fn fetch_data_from_backend() -> String {
// Логика получения данных с бэкенда
"Hello, from the backend!".to_string()
}
// Warp and WebSocket
async fn handle_websocket(ws: warp::ws::Ws, _: warp::ws::Message, _: HttpRequest) -> Result<(), warp::Error> {
Ok(())
// Логика обработки WebSocket-соединения
}
// WASM
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Rocket main
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(Template::fairing())
.mount("/", routes![index])
}
// Actix main
#[actix_web::main]
async fn actix_main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(web::resource("/api").to(api_handler))
})
.bind("127.0.0.1:8081")?
.run()
.await
}
// Warp main
#[tokio::main]
async fn warp_main() {
let ws_route = warp::path!("ws")
.and(warp::ws())
.map(|ws: warp::ws::Ws| {
ws.on_upgrade(|websocket| handle_websocket(websocket));
});

warp::serve(ws_route).run(([127, 0, 0, 1], 3030)).await;
}
fn main() {
// Запуск всех компонентов
std::thread::spawn(|| {
rocket().launch();
});
std::thread::spawn(|| {
actix_main().unwrap();
});
warp_main();
}