Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: zelenprog  
Дата: 13.05.24 14:29
Оценка: -1
Здравствуйте!

Объясните, пожалуйста, про "организацию" работы "начальной точки" (корня компоновки) веб-приложения.
По сути, веб-приложение — это на самом деле два отдельных приложения: веб-клиент и веб-сервис, которые обмениваются между собой запросами-сообщениями.

Каждое приложение должно начинаться с корня компоновки.
Значит, и работа веб-сервиса также должна начинаться с корня компоновки.
Однако, работа веб-сервиса зависит от поступившего запроса: для одного запроса выполняется одно действие, для другого запроса — другое действие...
Получается, что "начальная точка" веб-сервиса зависит от текста запроса.
То есть сначала должен быть какой-то предварительный синтаксический разбор запроса в корне компоновки перед созданием объектов-контроллеров, обрабатывающих запрос.
После обработки запроса, результат надо "упаковать" и отправить клиенту.

Значит, в начальной точке веб-сервиса должно быть создано несколько объектов:
— объект синтаксического разбора (распаковки) запроса
— объект-контроллер для обработки запроса
— объект упаковки результата
— и много других объектов, которые создаются в корне компоновки, которые необходимы для работы сервиса

Как правильно спроектировать эту начальную точку веб-сервиса, чтобы в ней выполнялись описанные действия?
Какие объекты надо создавать и как они должны взаимодействовать друг с другом?
Есть какие-нибудь готовые наработанные практические паттерны? Где про это можно почитать?
Re: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: Sharov Россия  
Дата: 13.05.24 14:50
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Как правильно спроектировать эту начальную точку веб-сервиса, чтобы в ней выполнялись описанные действия?

Z>Какие объекты надо создавать и как они должны взаимодействовать друг с другом?
Z>Есть какие-нибудь готовые наработанные практические паттерны? Где про это можно почитать?

Обычно этим занимаются соотв. фреймворки под каждый язык и\или платформу. Для .net читайте про asp .net.
Есть asp .net mvc (по сути полноценный веб сервер), есть просто веб-сервис web api, с которым идет
взаимодействие по http. В общем, у каждого такого фреймворка есть документация с кучей примеров, с нее и
начинайте.
Кодом людям нужно помогать!
Re: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: RushDevion Россия  
Дата: 13.05.24 18:38
Оценка: 7 (2)
Z>По сути, веб-приложение — это на самом деле два отдельных приложения: веб-клиент и веб-сервис, которые обмениваются между собой запросами-сообщениями.
Эээ... интересная постановка вопроса
Ну как бы да, серверная и клиентская части — это совершенно разные приложения.
Единственно, что их связывает — это протокол прикладного уровня. Стандартный — HTTP(s), FTP, SMTP, SSH и т.п., либо какой-то кастомный — REST over HTTP(s), SOAP over HTTP(s), GRPC over HTTP2 и т.п.
И пишут их зачастую разные люди и даже на разных языках программирования.

Z>Значит, в начальной точке веб-сервиса должно быть создано несколько объектов:

Z>- объект синтаксического разбора (распаковки) запроса
Z>- объект-контроллер для обработки запроса
Z>- объект упаковки результата
Z>- и много других объектов, которые создаются в корне компоновки, которые необходимы для работы сервиса

Собственно, нет. В том смысле, что эти объекты, конечно, нужны, но создавать каждый из них в явном виде в точке сборки не надо.
Для серверной части main может быть вот таким простым:
// Собственно, вот и весь composition root.
var config = ReadConfigFromSomeWhere();
var webServer = new WebServer(new WebServerOptions { BindAddress = config.ListenAddress, Port = config.ListenPort }, new MyRouter());
webServer.Start();
// А уже в конструкторе WebServer может быть что-то вроде:
//  this._router = router; // IRouter, который приходит как параметр конструктора
//  this._socket = new Socket(); // сокет, на котором слушаются входящие подключения
//  this._inputBuffer = new Buffer();   // Буфер для входящих данных
//  this._outputBuffer = new Buffer();  // Буфер для исходящих данных
//  this._parser = new HttpProtocolParser(this._inputBuffer); // Парсер http-протокола
//  и т.д. и т.п.
//
//  WebServer будет оперировать этими внутренними объектами, получать сырые байты, разбирать их в объекты HttpRequest и отдавать на обработку в заданный снаружи IRouter.Handle(httpRequest, httpResponse).

Далее в реализации роутера может быть, например, так:
public class MyRouter: IRouter {
  private JsonSerializer _json = new();  // Вот здесь объекты тоже создаются уже "внутри" реализации, нет необходимости тащить их в composition root
  private QueryStringParser _qsParser = new(); 

  public void Process(HttpRequest httpRequest, HttpResponse httpResponse) {
    object responseObj = ((httpRequest.Method, httpRequest.Path)) switch {
      // Здесь снова конкретный обработчик (контроллер) создается по месту, а не в composition root
      case ("POST", "api/users") => new UsersController().CreateUser(_json.Deserialize<CreateUserRequestDto>(request.Body)); 
      case ("GET", "api/users") => new UsersController().ListUsers(_qsParser.Parse<UsersFilterDto>(request.QueryString));
      case ("POST", "api/orders") => new OrdersController().PlaceOrder(_json.Deserialize<PlaceOrderRequest>(request.Body));
      _ => throw new NotFoundException();
    }
    httpResponse.SetStatusCode(200);
    httpResponse.Write(_json.Serialize(responseObj));
  }
}
Re[2]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: zelenprog  
Дата: 14.05.24 08:45
Оценка:
RD>Ну как бы да, серверная и клиентская части — это совершенно разные приложения.
RD>Единственно, что их связывает — это протокол прикладного уровня. Стандартный — HTTP(s), FTP, SMTP, SSH и т.п., либо какой-то кастомный — REST over HTTP(s), SOAP over HTTP(s), GRPC over HTTP2 и т.п.
RD>И пишут их зачастую разные люди и даже на разных языках программирования.

Ага.
Значит, упаковка-распаковка данных запроса клиента (или данных ответа сервера) для передачи по протоколу — это можно считать слоем инфраструктуры.
Верно?

Сейчас, как я понял, более распространенным является REST.

RD>Собственно, нет. В том смысле, что эти объекты, конечно, нужны, но создавать каждый из них в явном виде в точке сборки не надо.

RD>Для серверной части main может быть вот таким простым:
RD>
RD>// Собственно, вот и весь composition root.
RD>var config = ReadConfigFromSomeWhere();
RD>var webServer = new WebServer(new WebServerOptions { BindAddress = config.ListenAddress, Port = config.ListenPort }, new MyRouter());
RD>webServer.Start();
RD>// А уже в конструкторе WebServer может быть что-то вроде:
RD>//  this._router = router; // IRouter, который приходит как параметр конструктора
RD>//  this._socket = new Socket(); // сокет, на котором слушаются входящие подключения
RD>//  this._inputBuffer = new Buffer();   // Буфер для входящих данных
RD>//  this._outputBuffer = new Buffer();  // Буфер для исходящих данных
RD>//  this._parser = new HttpProtocolParser(this._inputBuffer); // Парсер http-протокола
RD>//  и т.д. и т.п.
RD>//
RD>//  WebServer будет оперировать этими внутренними объектами, получать сырые байты, разбирать их в объекты HttpRequest и отдавать на обработку в заданный снаружи IRouter.Handle(httpRequest, httpResponse). 
RD>

RD>Далее в реализации роутера может быть, например, так:
RD>
RD>public class MyRouter: IRouter {
RD>  ...
RD>}
RD>


А если я делаю свою собственную реализацию классов WebServer, IRouter, ProtocolParser, ...
Есть какая-нибудь общепринятая схема\архитектура\паттерн как должны взаимодействовать эти объекты?
И как они должны быть устроены "внутри"?
Re[3]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: Sharov Россия  
Дата: 14.05.24 09:12
Оценка:
Здравствуйте, zelenprog, Вы писали:


Z>Ага.

Z>Значит, упаковка-распаковка данных запроса клиента (или данных ответа сервера) для передачи по протоколу — это можно считать слоем инфраструктуры.
Z>Верно?

Да.


Z>А если я делаю свою собственную реализацию классов WebServer, IRouter, ProtocolParser, ...

Z>Есть какая-нибудь общепринятая схема\архитектура\паттерн как должны взаимодействовать эти объекты?

Едва ли будет возможность определить, как эти объекты будут взаимодействовать. Это уже проще с нуля самому написать.
Картинку как взаимодействие устроено внутри и в целом можно найти в гугле по ключевым словам и поиску по картинке.
Свои реализации иметь можно, но это просто реализации соотв. интерфейсов скорее всего. А как и для чего нужны соотв.
интерфейсы надо читать в доке.

Z>И как они должны быть устроены "внутри"?


Надо читать соотв. документацию. В целом, решения скорее всего будут типовые: компонент принимает входящий запрос,
парсит его, типизирует, вызывает соотв. обработчик (метод), получает результат, обрабатывает его как-то и возвращает
в сеть адресату.
Кодом людям нужно помогать!
Re[3]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: RushDevion Россия  
Дата: 14.05.24 10:18
Оценка: 5 (1)
Z>Ага.
Z>Значит, упаковка-распаковка данных запроса клиента (или данных ответа сервера) для передачи по протоколу — это можно считать слоем инфраструктуры.
Формально — да.
А на практике гексогональную архитектуру придумали, чтобы абстрагироваться от инфраструктуры.
Типа есть domain (переносимая между разными приложениями бизнес-логика), есть application-слой (use-кейсы, специфичные для конкретного приложения).
Вот в этих двух слоях — главная сложность корпоративного ПО, на которую следует тратить максимум усилий.
А все инфраструктурные вещи (web service, БД, кэши, отправка SMS/Email, общение с внешними системами) нужно прятать за интерфейсами, а для реализации, по-возможности, не заморачиваться, а использовать что-то готовое.

Z>А если я делаю свою собственную реализацию классов WebServer, IRouter, ProtocolParser, ...

Z>Есть какая-нибудь общепринятая схема\архитектура\паттерн как должны взаимодействовать эти объекты?
Z>И как они должны быть устроены "внутри"?
А как ты себе эту "общепринятую архитектуру" представляешь?
Типа UML диаграмма с классами? Ты ее взял, классы написал на своем ЯП и вот у тебя готовый канонический web-сервер?
Вряд ли что-то такое есть.

А "как должны взаимодействовать эти объекты" — это вопрос из серии "как правильно спроектировать ПО".
Об этом умные дяди большие книги пишут — про ООП, SOLID, low coupling/hight cohesion и т.п.
Re[4]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: zelenprog  
Дата: 14.05.24 11:56
Оценка:
Z>>Ага.
Z>>Значит, упаковка-распаковка данных запроса клиента (или данных ответа сервера) для передачи по протоколу — это можно считать слоем инфраструктуры.
RD>Формально — да.
RD>А на практике гексогональную архитектуру придумали, чтобы абстрагироваться от инфраструктуры.
RD>Типа есть domain (переносимая между разными приложениями бизнес-логика), есть application-слой (use-кейсы, специфичные для конкретного приложения).
RD>Вот в этих двух слоях — главная сложность корпоративного ПО, на которую следует тратить максимум усилий.
RD>А все инфраструктурные вещи (web service, БД, кэши, отправка SMS/Email, общение с внешними системами) нужно прятать за интерфейсами, а для реализации, по-возможности, не заморачиваться, а использовать что-то готовое.

Да, общую схему-архитектуру, я понимаю.
Просто в том приложении, которое я делаю, в этой среде разработки, ничего готового нету.
Надо сделать самому.

Z>>А если я делаю свою собственную реализацию классов WebServer, IRouter, ProtocolParser, ...

Z>>Есть какая-нибудь общепринятая схема\архитектура\паттерн как должны взаимодействовать эти объекты?
Z>>И как они должны быть устроены "внутри"?
RD>А как ты себе эту "общепринятую архитектуру" представляешь?
RD>Типа UML диаграмма с классами? Ты ее взял, классы написал на своем ЯП и вот у тебя готовый канонический web-сервер?

Ну вообще, да.
Надеялся, что есть какая-нибудь книга\статья, в которой описано общее решение с UML-диаграммами и с примерами кода.

RD>А "как должны взаимодействовать эти объекты" — это вопрос из серии "как правильно спроектировать ПО".

RD>Об этом умные дяди большие книги пишут — про ООП, SOLID, low coupling/hight cohesion и т.п.

Про общие "умные" принципы я читал. И сейчас читаю, изучаю.
Я имел ввиду, что если есть уже разработанные хорошие-проверенные-качественные решения, то хотелось бы посмотреть как они сделаны.
Ведь по сути общая схема работы для большинства "канонических" web-серверов скорее всего будет одинаковой.
Интересно, как они спроектированы, какие в них классы-объекты используются, как они взаимодействуют. Хотелось посмотреть именно конкретные решения.
Это то, что выше мы обсуждали про UML-диаграммы.
Re[5]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: RushDevion Россия  
Дата: 14.05.24 12:38
Оценка: +1
Z>Да, общую схему-архитектуру, я понимаю.
Z>Просто в том приложении, которое я делаю, в этой среде разработки, ничего готового нету.
Z>Надо сделать самому.

Сделать полноценный HTTP-сервер с нуля — это весьма нетривиальная задача, не говоря уж о временнЫх затратах на это дело.
Что это за экзотическая среда такая, где нет какой-то дефолтной или open-source реализации HTTP-сервера?

Z>Надеялся, что есть какая-нибудь книга\статья, в которой описано общее решение с UML-диаграммами и с примерами кода.


Я такого не встречал.
Но ты можешь загуглить что-то вроде "Open source HTTP server <твое_окружение>", наверняка найдется что-то готовое.
Re[6]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: zelenprog  
Дата: 14.05.24 14:14
Оценка:
Здравствуйте, RushDevion, Вы писали:

Z>>Да, общую схему-архитектуру, я понимаю.

Z>>Просто в том приложении, которое я делаю, в этой среде разработки, ничего готового нету.
Z>>Надо сделать самому.

RD>Сделать полноценный HTTP-сервер с нуля — это весьма нетривиальная задача, не говоря уж о временнЫх затратах на это дело.


У нас наверно путаница с терминологией.
HTTP-сервер у меня есть — это NGINX.
Мне нужно сделать ... (не знаю как правильно называется) ... веб-службу\сервер\приложение, которое работает с веб-клиентом через NGINX.

Есть исходники программ, написанных на Rust.
Вот например в одной из них сделано вот так:

fn main() {
    env_logger::init();
    log::info!("Запуск сервиса DESADV");
    server_start();
}

pub async fn server_start() {
    let index = warp::path::end().map(handlers::handle_index);
    let log = warp::log("info");
    let truncate = warp::post().and(warp::path!("api"/"v1"/"truncate")).map(handlers::handle_truncate);
    let upload_ftp = warp::post().and(warp::path!("api"/"v1"/"upload")).map(handlers::handle_upload_to_ftp);
    ...

    let routes = data_dir
        .or(index)
        .or(upload_ftp)
        .or(truncate)
        .or(upload_upd)
        .with(log);

    warp::serve(routes)
        .run(([0, 0, 0, 0], HTTP_PORT))
        .await;
}
Re[7]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: RushDevion Россия  
Дата: 14.05.24 15:41
Оценка:
Z>У нас наверно путаница с терминологией.
Z>HTTP-сервер у меня есть — это NGINX.
Z>Мне нужно сделать ... (не знаю как правильно называется) ... веб-службу\сервер\приложение, которое работает с веб-клиентом через NGINX.

HTTP-сервер — это любое ПО, которое умеет принимать запросы по HTTP-протоколу.
NGINX — это HTTP-сервер, и служба на rust — это тоже HTTP-сервер.

Z>Есть исходники программ, написанных на Rust.

Z>Вот например в одной из них сделано вот так:
...

Ну и?
Я на Rust никогда не писал, но, код в целом понятен.
Тут используется некая библиотека warp.
Ненапряженное гугление приводит на github проекта.
> A super-easy, composable, web server framework for warp speeds.
Т.е. это как раз и есть http framework для Rust, который скрывает все низкоуровневые детали HTTP-протокола.
А верхнеуровнево имеем фактически тот же самый IRouter с Rust-спецификой: мапим пару HTTP-метод + путь на наши обработчики.
Re[8]: Шаблон распаковки веб-запроса (Корень компоновки для веб-приложения)
От: zelenprog  
Дата: 15.05.24 07:06
Оценка:
RD>NGINX — это HTTP-сервер, и служба на rust — это тоже HTTP-сервер.

Вот поэтому и недопонимание.
Надо бы IT-сообществу придумать специальные термины для этих двух понятий.
Типа NGINX — это "общий" (или "универсальный") HTTP-сервер, а наша служба — это "специальный" HTTP-сервер.

RD>Я на Rust никогда не писал, но, код в целом понятен.


Я тоже на Rust никогда не писал. И вообще с веб-технологиями никогда не сталкивался.
Писал только декстопные приложения, вроде работы с БД.

Поэтому приложение-обработчик HTTP-запросов для меня в новинку.
Идеология тут другая.

RD>Тут используется некая библиотека warp.

RD>Т.е. это как раз и есть http framework для Rust, который скрывает все низкоуровневые детали HTTP-протокола.
RD>А верхнеуровнево имеем фактически тот же самый IRouter с Rust-спецификой: мапим пару HTTP-метод + путь на наши обработчики.

Да, постепенно до меня доходит вся специфика веб-приложения. Например, что "верхнеуровнево" у нас должен быть IRouter.
Но это понимание идет медленно. В данном случае — это происходит на примере этой программы на Rust.

А мне хотелось бы что-то прочитать основательное по разработке подобных веб-приложений.
Чтобы там объяснялось от азов (типа технические детали обработки HTTP-запросов) до архитектуры веб-приложений в целом.
Я читал про DDD, про Чистую Архитектуру и т.д. — эти принципы архитектуры я понимаю.
Вот хорошо бы эти принципы "перенести" на веб-приложение.
Ведь применение общих принципов архитектуры для десктопного и для веб-приложения наверняка имеет какие-то свои особенности в каждом случае.
Re[2]: Шаблон распаковки веб-запроса (Корень компоновки для
От: zelenprog  
Дата: 15.05.24 07:19
Оценка: 14 (2)
RD>Ну как бы да, серверная и клиентская части — это совершенно разные приложения.
RD>Единственно, что их связывает — это протокол прикладного уровня. Стандартный — HTTP(s), FTP, SMTP, SSH и т.п., либо какой-то кастомный — REST over HTTP(s), SOAP over HTTP(s), GRPC over HTTP2 и т.п.
RD>И пишут их зачастую разные люди и даже на разных языках программирования.

Во!
Нашел статью, которая разъясняет специфику веб-прилоежний:
https://habr.com/ru/articles/500072/

Вот что-то типа такого хочется почитать.
Только еще бы подробнее, с примерами кода, в котором применяются паттерны проектирования.

Картинка из этой статьи:
Отредактировано 15.05.2024 7:21 zelenprog . Предыдущая версия . Еще …
Отредактировано 15.05.2024 7:20 zelenprog . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.