Демонстрация рендеринга на стороне сервера в Rust — пример использования Dall.E

Демонстрация рендеринга на стороне сервера в Rust — пример использования Dall.E

5 мая 2023 г.

На прошлой неделе я решил ознакомиться с возможностями создания изображений OpenAI. Однако я заметил, что за использование веб-интерфейса нужно платить, а API был бесплатным, хотя и с ограничением скорости. Dall.E предлагает образцы Node.js и Python, но я хотел продолжать изучать Rust. . На данный момент я создал REST API. В этом посте я хочу описать, как создать веб-приложение с рендерингом на стороне сервера.

Контекст

Tokio — это среда выполнения для асинхронного программирования на Rust; Axum — это веб-фреймворк, использующий первый. Я уже использовал Axum для предыдущего REST API, поэтому решил продолжить.

Веб-приложение для рендеринга на стороне сервера похоже на REST API. Единственное отличие состоит в том, что первый возвращает HTML-страницы, а второй — полезные данные JSON. С архитектурной точки зрения разницы нет; однако с точки зрения разработки это играет огромную роль.

В JSON нет визуальных требований, поэтому порядок не является проблемой. Вы получаете структуру; вы сериализуете его, и все готово. Вы даже можете сделать это вручную; ничего страшного - хоть и скучновато. С другой стороны, HTML требует точного порядка тегов: если вы создадите его вручную, обслуживание станет кошмаром. Мы изобрели шаблоны для создания кода с учетом порядка следования кода.

Хотя шаблоны, вероятно, устарели, PHP стал языком их популяризации. Один пишет обычный HTML и, при необходимости, добавляет фрагменты, которые необходимо динамически интерпретировать. В мире JVM я использовал JSP и Apache Velocity, последний, для создания RTF-документов.

Шаблоны в Axum

Как я уже упоминал выше, я хочу продолжать использовать Axum. Axum не предлагает готового решения для создания шаблонов, но позволяет интегрировать любое решение через свой API.

Вот небольшой пример библиотек шаблонов, которые я нашел для Rust:

* handlebars-rust, на основе Handlebars * liquid, на основе Liquid * Tera, основанный на Jinja, как следующие два * аскама * МиниДжинджа * и т.д.

Как разработчик, правда, я по сути ленив, и хотелось что-то интегрированное с Аксумом из коробки. Быстрый поиск в Google привел меня к axum-template, который кажется довольно новым, но очень динамичным. Библиотека представляет собой абстракцию над рулем, аскамой и мини-дзиндзей. Вы можете использовать API и изменять реализацию в любое время.

шаблон axum вкратце

Настроить axum-template относительно просто. Во-первых, мы добавляем зависимость к Cargo:

cargo add axum-template

Затем мы создаем механизм в зависимости от базовой реализации и настраиваем Axum для его использования. Здесь я использую Jinja:

type AppEngine = Engine<Environment<'static>>;                 //1

#[derive(Clone, FromRef)]
struct AppState {                                              //2
    engine: AppEngine,
}

#[tokio::main]
async fn main() {
    let mut jinja = Environment::new();                        //3
    jinja.set_source(Source::from_path("templates"));          //4
    let app = Router::new()
        .route("/", get(home))
        .with_state(AppState {                                 //5
            engine: Engine::from(jinja),
        });
}
  1. Создайте псевдоним типа
  2. Создайте специальную структуру для хранения состояния движка.
  3. Создайте среду для Jinja
  4. Настройте папку для чтения шаблонов. Путь относится к месту, где вы запускаете двоичный файл; он не должен быть частью папки src. Я потратил нетривиальное количество времени, чтобы понять это.
  5. Настройте Axum для использования движка

Вот базовые элементы:

  • Движок — это фасад над библиотекой шаблонов
  • Шаблоны хранятся в структуре, похожей на хэш-таблицу. В реализации MiniJinja, в соответствии с приведенной выше конфигурацией, Key — это просто имя файла, например,, home.html
  • Последний параметр S не является обязательным. Библиотека прочитает свои атрибуты и использует их для заполнения шаблона.

Я не буду вдаваться в подробности самого шаблона, так как документация довольно хороша.

Возврат

Это не имеет ничего общего с шаблонами, но этот мини-проект позволил мне обдумать возвращаемый тип impl. В моем предыдущем проекте REST я заметил, что функции-обработчики Axum возвращают impl, но я не думал об этом. Это действительно довольно просто:

<цитата>

Если ваша функция возвращает тип, реализующий MyTrait, вы можете указать тип возвращаемого значения как -> внедрить MyTrait. Это может значительно упростить подписи типов!

-- Rust на примере

Однако у него есть интересные последствия. Если вы возвращаете один тип, это работает как шарм. Однако, если вы вернете более одного, вам потребуется либо общая черта для всех возвращаемых типов, либо явное указание на нее.

Вот исходный образец:

async fn call(engine: AppEngine, Form(state): Form<InitialPageState>) -> impl IntoResponse {
    RenderHtml(Key("home.html".to_owned()), engine, state)
}

Если состояние страницы должно различать успех и ошибку, мы должны создать две специальные структуры.

async fn call(engine: AppEngine, Form(state): Form<InitialPageState>) -> Response {                     //1
  let page_state = PageState::from(state);
  if page_state.either.is_left() {
    RenderHtml(Key("home.html".to_owned()), engine, page_state.either.left().unwrap()).into_response()  //2
  } else {
    RenderHtml(Key("home.html".to_owned()), engine, page_state.either.right().unwrap()).into_response() //2
  }
}
  1. Невозможно использовать impl IntoResponse; необходимо использовать явный тип Response
  2. Явное преобразование возвращаемого значения в Response

Использование приложения

Вы можете выполнить сборку из исходного кода или запустить образ Docker, доступный на DockerHub. Единственное требование — предоставить токен аутентификации OpenAI через переменную среды:

docker run -it --rm -p 3000:3000 -e OPENAI_TOKEN=... nfrankel/rust-dalle:0.1.0

Наслаждайтесь!

Заключение

Этот небольшой проект позволил мне открыть для себя другую сторону Rust: шаблоны HTML с помощью Axum. Это не обычный вариант использования Rust, но в любом случае он является его частью.

Возможности Dall.E меня не особо впечатлили. Возможно, мне не удалось правильно описать результаты. Мне нужно улучшить свои инженерные навыки.

В любом случае, я рад, что разработал интерфейс, хотя бы для развлечения.

Полный исходный код для этого поста можно найти на GitHub.

Дальше:

:::информация Первоначально опубликовано по адресу A Java Geek, 30 апреля 2023 г.

:::


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE