Server-side Web
5 януари 2023
Административни неща
- Домашно 3: съществува https://fmi.rust-lang.bg/tasks/3
- Предложения за проекти: #projects в Discord
За боба, 'леба и уеба
- Rust е "използваем" за уеб: https://www.arewewebyet.org/
За боба, 'леба и уеба
- Rust е "използваем" за уеб: https://www.arewewebyet.org/
- Фреймуърците са по-скоро "библиотеки". Прости, композируеми. Има какво да се желае от интеграцията между тях.
За боба, 'леба и уеба
- Rust е "използваем" за уеб: https://www.arewewebyet.org/
- Фреймуърците са по-скоро "библиотеки". Прости, композируеми. Има какво да се желае от интеграцията между тях.
- Нов фреймуърк, който се движи от Luca Palmieri: "pavex". Има за цел да бъде повече "batteries included", но не е готов.
За боба, 'леба и уеба
Накратко как работи Интернета:
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
- Някой си отваря браузъра ("клиент") и търси нашия адрес и порт, който се превежда до IP адрес и порт от някакъв DNS (Domain Name Server).
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
- Някой си отваря браузъра ("клиент") и търси нашия адрес и порт, който се превежда до IP адрес и порт от някакъв DNS (Domain Name Server).
- Клиента праща определени параметри, базирано на които програмата-сървър решава какъв низ да върне.
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
- Някой си отваря браузъра ("клиент") и търси нашия адрес и порт, който се превежда до IP адрес и порт от някакъв DNS (Domain Name Server).
- Клиента праща определени параметри, базирано на които програмата-сървър решава какъв низ да върне.
- Този низ е във формат HTML, който браузъра знае как да интерпретира като структуриран текст
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
- Някой си отваря браузъра ("клиент") и търси нашия адрес и порт, който се превежда до IP адрес и порт от някакъв DNS (Domain Name Server).
- Клиента праща определени параметри, базирано на които програмата-сървър решава какъв низ да върне.
- Този низ е във формат HTML, който браузъра знае как да интерпретира като структуриран текст
- Или във формат CSS, който казва на браузъра "тази страница има шарени цветове и закръглени ръбчета"
За боба, 'леба и уеба
Накратко как работи Интернета:
- Една машина стартира безкраен цикъл, който чака за заявки с определен протокол (HTTP) на определен порт. Стандартния порт е 80 за некриптирани, 443 за криптирани връзки. Но може да се използва който и да е.
- Някой си отваря браузъра ("клиент") и търси нашия адрес и порт, който се превежда до IP адрес и порт от някакъв DNS (Domain Name Server).
- Клиента праща определени параметри, базирано на които програмата-сървър решава какъв низ да върне.
- Този низ е във формат HTML, който браузъра знае как да интерпретира като структуриран текст
- Или във формат CSS, който казва на браузъра "тази страница има шарени цветове и закръглени ръбчета"
- Или във формат Javascript, който инструктира браузъра да кара текста да мига и да копае биткойни.
За боба, 'леба и уеба
Сървъра е просто един цикъл, който чака низова информация в определен формат и връща низова информация в определен формат. Може да го напишем на shellscript, ако искаме (но ще го пишем на Rust).
Разбира се, в реални условия е доста по-сложно да се докарат всички детайли.
(Тия обяснения вероятно не са достатъчни за начинаещ, но поне не са нищо. ¯\_(ツ)_/¯
)
Hello Web
Demo
Версии:
[dependencies]
actix-web = "4"
Бихме могли вместо #[actix_web::main]
да ползваме #[tokio::main]
с всичките стандартни инструменти на Tokio. Има някаква част от actix-web environment-а, която все още го изисква, но става въпрос за websockets, които са малко странична история.
Още детайли: #[actix_web::main]
and #[tokio::main]
Hello Web
Demo
- Съкратено изграждане на маршрут:
web::get()
==Route::new().guard(guard::Get())
- (Пример за използване на guard-ове: https://docs.rs/actix-web/4.2.1/actix_web/guard/index.html#examples)
Hello Web
Demo
- Съкратено изграждане на маршрут:
web::get()
==Route::new().guard(guard::Get())
- (Пример за използване на guard-ове: https://docs.rs/actix-web/4.2.1/actix_web/guard/index.html#examples)
- "Route": Комбинация от функция и guard-ове, които описват кога се активира, примерно
web::get().to(render_404)
Hello Web
Demo
- Съкратено изграждане на маршрут:
web::get()
==Route::new().guard(guard::Get())
- (Пример за използване на guard-ове: https://docs.rs/actix-web/4.2.1/actix_web/guard/index.html#examples)
- "Route": Комбинация от функция и guard-ове, които описват кога се активира, примерно
web::get().to(render_404)
- "Resource": Няколко route-а могат да се накачулят на един път, примерно
web::resource("/posts")
може да му се добавят няколко route-а с различни guard-ове. (Пример след малко)
Hello Web
Demo
- Съкратено изграждане на маршрут:
web::get()
==Route::new().guard(guard::Get())
- (Пример за използване на guard-ове: https://docs.rs/actix-web/4.2.1/actix_web/guard/index.html#examples)
- "Route": Комбинация от функция и guard-ове, които описват кога се активира, примерно
web::get().to(render_404)
- "Resource": Няколко route-а могат да се накачулят на един път, примерно
web::resource("/posts")
може да му се добавят няколко route-а с различни guard-ове. (Пример след малко) - "Extractor": Тип, който имплементира
FromRequest
-- може да вадим форми, сесия, параметри от request-а.
Hello Web
Demo
- Съкратено изграждане на маршрут:
web::get()
==Route::new().guard(guard::Get())
- (Пример за използване на guard-ове: https://docs.rs/actix-web/4.2.1/actix_web/guard/index.html#examples)
- "Route": Комбинация от функция и guard-ове, които описват кога се активира, примерно
web::get().to(render_404)
- "Resource": Няколко route-а могат да се накачулят на един път, примерно
web::resource("/posts")
може да му се добавят няколко route-а с различни guard-ове. (Пример след малко) - "Extractor": Тип, който имплементира
FromRequest
-- може да вадим форми, сесия, параметри от request-а. - "Service": Малко странен термин, който се среща досадно често в документацията без да бъде добре обяснен. Абстракция за нещо, което приема request и връща response: "You can think about a service as a function with one argument that returns some result asynchronously".
- Нашите handler-и ще бъдат пакетирани в някакъв код, за да се сведат до "service".
Actix-web
Extractor magic: https://docs.rs/actix-web/4.2.1/actix_web/trait.Handler.html.
Spotiferris
Ще разгледаме (началото на) малък проект за хостинг на музика. Stack-а:
Database
cargo install sqlx-cli --no-default-features --features rustls,postgres
Gotchas
match form.insert(&db) {
Ok(id) => {
error[E0308]: mismatched types
--> src/handlers.rs:86:13
|
86 | Ok(id) => {
| ^^^^^^ expected opaque type, found enum `std::result::Result`
|
::: src/models.rs:40:48
|
40 | pub async fn insert(&self, db: &PgPool) -> Result<i32, sqlx::Error> {
| ------------------------ the `Output` of this `async fn`'s expected opaque type
|
= note: expected opaque type `impl futures::Future`
found enum `std::result::Result<_, _>`
Gotchas
match form.insert(&db).await {
Ok(id) => {
All good!
Gotchas
Ако забравим async
за handler-а, грешката не е много готина:
pub fn index(db: web::Data<PgPool>) -> HttpResponse {
route("/", web::get().to(handlers::songs::index)).
-- ^^^^^^^^^^^^^^^^^^^^^^ the trait `Handler<_>` is not implemented for fn item `fn(Data<Pool<Postgres>>) -> actix_web::HttpResponse {songs::index}`
|
required by a bound introduced by this call
Axum (друг фреймуърк) има #[axum::debug_handler]
макрос, който помага с грешките, което е някакво подобрение, I guess.
Gotchas
Разни компилаторни грешки може да препоръчат NamedFile
+ статус код да се имплементира с customize().with_status(
. Хубаво, ама не става съвсем:
pub async fn render_404(request: HttpRequest) -> Result<HttpResponse> {
let file = NamedFile::open("static/404.html")?;
Ok(file.customize().with_status(StatusCode::NOT_FOUND).respond_to(&request))
error[E0308]: mismatched types
--> src/handlers.rs:24:8
|
24 | Ok(file.customize().with_status(StatusCode::NOT_FOUND).respond_to(&request))
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `BoxBody`, found enum `EitherBody`
| |
| arguments to this enum variant are incorrect
|
= note: expected struct `actix_web::HttpResponse<BoxBody>`
found struct `actix_web::HttpResponse<EitherBody<BoxBody>>`
Gotchas
Ако пробваме impl Responder
? Пак не става:
pub async fn render_404(request: HttpRequest) -> Result<impl Responder> {
let file = NamedFile::open("static/404.html")?;
Ok(file.customize().with_status(StatusCode::NOT_FOUND).respond_to(&request))
error[E0308]: mismatched types
--> src/handlers.rs:126:23
|
22 | pub async fn render_404(request: HttpRequest) -> Result<impl Responder> {
| -------------- the found opaque type
...
126 | Err(_) => render_404(request).await,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `actix_web::HttpResponse`, found opaque type
|
= note: expected enum `std::result::Result<actix_web::HttpResponse, _>`
found enum `std::result::Result<impl Responder, _>`
Gotchas
Твърде много типова магия. Едно решение е да спрем да се лигавим с композиране на типове и просто да си направим response, който да мутираме:
pub async fn render_404(request: HttpRequest) -> Result<HttpResponse> {
let file = NamedFile::open_async("static/404.html").await?;
let mut response = file.into_response(&request);
*response.status_mut() = StatusCode::NOT_FOUND;
Ok(response)
}
Auto-reload
cargo install cargo-watch
cargo watch -x 'run --bin server'
Testing
Най-лесно ако използвате sqlx е #[sqlx::test]
анотацията на тестовете: https://docs.rs/sqlx/latest/sqlx/attr.test.html.
Проблема е, че ако просто си инстанцирате тестова база данни на ръка (което е вариант), тестовете ще вървят паралелно и ще я мажат заедно. Може да го спрете с флаг на cargo test (или с този crate: https://crates.io/crates/serial_test):
cargo test -- --test-threads=1
Но нали, ако можете да ползвате sqlx::test
, best to just go for it. Метода му е същия, който Luca Palmieri описва в книгата си.
Spotiferris
Проблеми
- Интеграцията между библиотеките иска работа.
Spotiferris
Проблеми
- Интеграцията между библиотеките иска работа.
- Повечето библиотеки са версия 0.x -- нестабилни са като интерфейс.
Spotiferris
Проблеми
- Интеграцията между библиотеките иска работа.
- Повечето библиотеки са версия 0.x -- нестабилни са като интерфейс.
- Типовата магия може да е трудна за дебъгване.
Spotiferris
Проблеми
- Интеграцията между библиотеките иска работа.
- Повечето библиотеки са версия 0.x -- нестабилни са като интерфейс.
- Типовата магия може да е трудна за дебъгване.
- Тестването е ръбато.
Spotiferris
Проблеми
- Интеграцията между библиотеките иска работа.
- Повечето библиотеки са версия 0.x -- нестабилни са като интерфейс.
- Типовата магия може да е трудна за дебъгване.
- Тестването е ръбато.
- Проекта е далеч от "production-ready"…
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
- Actix си има доста добра документация: https://actix.rs/docs/. Има и actix-examples с разнообразни интересни неща.
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
- Actix си има доста добра документация: https://actix.rs/docs/. Има и actix-examples с разнообразни интересни неща.
- Axum е почти идентичен с actix-web откъм интерфейс: https://github.com/tokio-rs/axum. Главния му selling point е че е базиран отгоре на tokio/hyper екосистемата, така че използва съществуващ middleware, вместо да има нови пакети за него. Набира скорост.
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
- Actix си има доста добра документация: https://actix.rs/docs/. Има и actix-examples с разнообразни интересни неща.
- Axum е почти идентичен с actix-web откъм интерфейс: https://github.com/tokio-rs/axum. Главния му selling point е че е базиран отгоре на tokio/hyper екосистемата, така че използва съществуващ middleware, вместо да има нови пакети за него. Набира скорост.
- Diesel е друг фреймуърк за бази данни. Малко по-battery-included, но със собствените си особености: https://diesel.rs/
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
- Actix си има доста добра документация: https://actix.rs/docs/. Има и actix-examples с разнообразни интересни неща.
- Axum е почти идентичен с actix-web откъм интерфейс: https://github.com/tokio-rs/axum. Главния му selling point е че е базиран отгоре на tokio/hyper екосистемата, така че използва съществуващ middleware, вместо да има нови пакети за него. Набира скорост.
- Diesel е друг фреймуърк за бази данни. Малко по-battery-included, но със собствените си особености: https://diesel.rs/
- Още за бази:
Ресурси
- Luca Palmieri има страхотна поредица от blog post-ове, в която задълбава в още доста детайли: https://www.lpalmieri.com/. Компилира ги в книга, "Zero to Production".
- Actix си има доста добра документация: https://actix.rs/docs/. Има и actix-examples с разнообразни интересни неща.
- Axum е почти идентичен с actix-web откъм интерфейс: https://github.com/tokio-rs/axum. Главния му selling point е че е базиран отгоре на tokio/hyper екосистемата, така че използва съществуващ middleware, вместо да има нови пакети за него. Набира скорост.
- Diesel е друг фреймуърк за бази данни. Малко по-battery-included, но със собствените си особености: https://diesel.rs/
- Още за бази:
- Askama: https://djc.github.io/askama/askama.html
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
- Кръстословици: https://youtu.be/9aHfK8EUIzg (линка е по-скоро за неща като data storage, и като цяло е интересна история)
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
- Кръстословици: https://youtu.be/9aHfK8EUIzg (линка е по-скоро за неща като data storage, и като цяло е интересна история)
- Meme generator, gif search (с upload и текстово описване на гифове)?
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
- Кръстословици: https://youtu.be/9aHfK8EUIzg (линка е по-скоро за неща като data storage, и като цяло е интересна история)
- Meme generator, gif search (с upload и текстово описване на гифове)?
- "Тънък" web проект -- web нещата да са само за интерфейс, с интересна логика отвъд това.
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
- Кръстословици: https://youtu.be/9aHfK8EUIzg (линка е по-скоро за неща като data storage, и като цяло е интересна история)
- Meme generator, gif search (с upload и текстово описване на гифове)?
- "Тънък" web проект -- web нещата да са само за интерфейс, с интересна логика отвъд това.
- Или, "истински" web проект с база данни, формички, CRUD интерфейси и т.н.
Проекти?
- Multiplayer snake: https://youtu.be/Yb-QR3Vm3sk
- Кръстословици: https://youtu.be/9aHfK8EUIzg (линка е по-скоро за неща като data storage, и като цяло е интересна история)
- Meme generator, gif search (с upload и текстово описване на гифове)?
- "Тънък" web проект -- web нещата да са само за интерфейс, с интересна логика отвъд това.
- Или, "истински" web проект с база данни, формички, CRUD интерфейси и т.н.
- Няма да сме много взискателни откъм production-readiness, webscale, etc. Подкарайте го да върви в development mode, да има тестове, да спазва някакви web идиоми, доколкото ги разбирате, и ще сме доволни.