axial 0.2.0

Axial - All in One web framework for Rust
Documentation
# axial-rs


Axial - All in One веб фреймворк для Rust.

> [!NOTE]
> All in One - всё самое нужное для разработки веб серверов в одном фреймворке

Фреймворк вдохновлён [Ruby on Rails](https://github.com/rails/rails)

## Использование


Для указания маршрутов сервера используется макрос `#[axial_macros::get("/path")]` (либо другой тип)
Далее функция, которая описывает этот маршрут:
```rust
#[get("/user/{id}")]

async fn get_user_details(req: Request) -> impl Responder {
    let user_id_str = req.path_params.get("id").cloned().unwrap_or_default();

    let version_str = req.query_param("version");

    let mut response_body = format!("User ID: {}", user_id_str);
    if let Some(v) = version_str {
        response_body.push_str(&format!(", Version (from query): {}", v));
    } else {
        response_body.push_str(", Version (from query): not specified");
    }

    Response::new(200).body(response_body)
}
```

`async`  и возврат `impl Responder` - **обязательно!**

Код довольно похож на [actix_web](https://github.com/actix/actix-web), но цель моего фреймворка - поместить всё самое необходимое в одно место, предоставив удобный API.

### Структуры


**Response**:

Данная структура предоставляет доступ к ответу на запрос. Методы:

* **body(String)** - установка тела запроса;

* **status(u16)** - статус ответа;

* **header(impl Into<String>, value: impl Into<String>)** - установка заголовка ответа;

* **cookie(impl Into<String>, value: impl Into<String>)** - установка куки к ответу;

* **new(u16)** - конструктор ответа. На вход получает статус ответа.

все функции возвращают структуру Response.

---

**Request**:

Данная структура предоставляет доступ к запросу. Получить данные с конкретного запроса можно так:

`async fn greet(req: Request) -> impl Responder`

`req` даст вам доступ к полям запроса:

* **body** - доступ к телу запроса. Вернёт String;

* **headers** - доступ к заголовкам запроса. Вернёт Vec<(String, String)>;

* **method** - доступ к методу, по которому пришёл запрос. Вернёт enum Methods;

`pub enum Methods {
    GET,
    POST,
    PUT,
    DELETE,
}`

* **path** - доступ к пути запроса. Вернёт String;

* **path_params** - доступ к полям пути (те, что вы указзываете через {name} в пути у макроса: `#[get("/user/{id}` = `/user/1`). `Вернёт Arc<HashMap<String, String, RandomState>, Global>`;

* **query_string** - доступ к параметрам пути (те, что указываются в запросе через `?`: `/user?name=somename`. Вернёт Option<String>

### Запуск сервера


```rust
HttpServer::new(String::from("127.0.0.1"), 9092).service(get_user_details)
        .service(post_user_details).start()
        .await.map_err(|e| {
            eprintln!("Error on start server: {e}")
        }).unwrap();
```

Конструктор сервера вызывается через HttpServer::new(host: String, port: u16);

Каждая функция-роут указываются через функцию `.service()` (1 функция = 1 вызов `service()`);

Сервер запускатеся функцией `start()` - асинхронная функция, может вернуть ошибку, поэтому её следует обработать в коде.

---

## Клиент


*Доступ к функциональности клиента можно получить, используя feature = "client".*

Пример:

```rust
async fn client() -> String {
    let client = HttpClient::new()
        .timeout(Some(std::time::Duration::from_secs(5)))
        .user_agent(Some(axial::core::clients::http::USER_AGENT_CHROME.to_string()))
        .header("X-Custom-Header", "value")
        .build()
        .unwrap();

    let response = client.get("https://google.com/").await.unwrap();

    response
}
```

* `HttpClient::new()` - вызов конструктора клиента;

* `timeout(Option<std::time::Duration>)` - указывает таймаут;

* `user_agent(Option<String>)` - указывает юзер агента для запроса. У фреймворка есть (на момент публикации) две константы (под разые ОС доступны по одному имени): USER_AGENT_CHROME и USER_AGENT_FIREFOX.

```rust
#[cfg(target_os = "windows")] pub const USER_AGENT_CHROME: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";

#[cfg(target_os = "linux")] pub const USER_AGENT_CHROME: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";

#[cfg(target_os = "macos")] pub const USER_AGENT_CHROME: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";


#[cfg(target_os = "windows")] pub const USER_AGENT_FIREFOX: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0";

#[cfg(target_os = "macos")] pub const USER_AGENT_FIREFOX: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14.7; rv:128.0) Gecko/20100101 Firefox/128.0";

#[cfg(target_os = "linux")] pub const USER_AGENT_FIREFOX: &str = "Mozilla/5.0 (X11; Linux i686; rv:128.0) Gecko/20100101 Firefox/128.0";

```

* `build()` - соберёт клиента. Возвращает Result<HttpClient, String>, поэтому надо обработать ошибку.

Вы можете отправлять запросы:

* `client.get(url: &str)` - get запрос на ссылку. Функция получает (кроме ссылки) `self`, поэтому в примере стоит `client: HttpClient`;

* `client.post(url: &str, body: &String)` - post запрос на ссылку (с телом);

* `client.put(url: &str, body: &String)` - put запрос на ссылку (с телом);

* `client.delete(url: &str)` - delete запрос на ссылку.

Все функции асинхронные и возвращают Result<String, String>- тело ответа.

---

Немного по поводу атрибута к роутам:

Этот `get`, `post` и тд - процедурный макрос, который переводит вашу асинхронную функцию в структуру и сделает для неё реализацию, которая далее пойдёт в `core::routes::router::RouteFactory` и адаптирована для сервера. В `serivce()` передаётся как раз generic тип routes::router::RouteFactory.

---

## Установка


Классика:

* `cargo add axial`
* `cargo add axial_macros`

Если вам нужен также клиент:

* `cargo add axial --features=client`