В Питере шаверма и мосты, в Казани эчпочмаки и казан. А что в других городах?
Мы постарались сделать каждый город, с которого начинается еженедельный заед в нашей новой игре, по-настоящему уникальным. Оценить можно на странице совместной игры Torero и Пикабу.
Реклама АО «Кордиант», ИНН 7601001509
HTTP 267 - Сомнительно, но Окэй
Ссылка на великий проект: https://github.com/maximal/http-267
Как отправить человеку ссылку на сайт
Как отправить человеку ссылку на сайт через whatsapp если сайт не поддерживает https. Входе открытия ссылки ватсап добавляет "s" в http , и после этого сайт не открывается...
Помогите нубасу
Основы HTTP-серверов
В этой статье вы поближе познакомитесь с пакетом net/http и полезными сторонними библиотеками на примере построения простых серверов, маршрутизаторов и промежуточного ПО.
Создание простого сервера:
Код снизу запускает сервер, который обрабатывает запросы по одному пути. (Все листинги кода находятся в корне /exist репозитория GitHub https://github.com/blackhat-go/bhg/.) Этот сервер должен обнаруживать URL-параметр name, содержащий имя пользователя, и отвечать заданным приветствием.
Сервер Hello World
Этот простой пример предоставляет ресурс по адресу /hello. Данный ресурс получает параметр и возвращает его значение обратно клиенту. http.HandleFunc() в функции main() получает два аргумента: строку, являющуюся шаблоном URL-пути, который сервер должен искать, и функцию, которая будет обрабатывать сам запрос. При желании определение функции можно оформить в виде анонимной встроенной функции. В этом примере мы передаем определенную чуть раньше функцию hello().
Функция hello() обрабатывает запросы и возвращает клиенту сообщение «Hello». Она получает два аргумента. Первый — это http.ResponseWriter, используемый для записи ответов на запрос. Второй аргумент является указателем на http.Request, который позволит считывать информацию из входящего запроса. Обратите внимание на то, что мы не вызываем hello() из main(), а просто сообщаем HTTP-серверу, что любые запросы для /hello должны обрабатываться функцией hello().
Что же на самом деле происходит внутри http.HandleFunc()? В документации Go сказано, что она помещает обработчик в DefaultServerMux. ServerMux означает серверный мультиплексор. На деле же это просто сложное выражение, подразумевающее, что внутренний код может обрабатывать несколько HTTP-запросов для шаблонов и функций. Это выполняется посредством горутин, по одной для каждого запроса. При импорте пакета net/http создается ServerMux и прикрепляется к пространству имен этого пакета. Это DefaultServerMux.
В следующей строке прописан вызов http.ListenAndServe(), которая получает в качестве аргументов строку и http.Handler. Она запускает HTTP-сервер, используя первый аргумент в роли адреса, которым в данном случае является :8000. Это означает, что сервер должен прослушивать порт 8000 по всем интерфейсам. Для второго аргумента, http.Handler, передается nil. В результате пакет задействует в качестве обработчика DefaultServerMux. Вскоре мы будем реализовывать собственный http.Handler и передавать его, но пока что используем предустановленный вариант. Можно также задействовать http.ListenAndServeTLS(), которая запустит сервер с использованием HTTPS и TLS, но потребует дополнительных параметров.
Для реализации интерфейса http.Handler необходим один метод — ServeHTTP(http.ResponseWriter, *http.Request). И это здорово, потому что упрощается создание собственных специализированных HTTP-серверов. Существует множество сторонних реализаций, которые расширяют функциональность пакета net/http, добавляя такие возможности, как промежуточное ПО, аутентификация, кодирование ответа и др.
Протестировать созданный сервер можно с помощью curl:
Превосходно! Этот сервер считывает URL-параметр name и отвечает приветствием.
Создание простого маршрутизатора:
Далее мы создадим простой маршрутизатор, приведенный , который показывает, как динамически обрабатывать входящие запросы, проверяя URL-путь. В зависимости от того, что содержит URL-путь, /a, /b или /c, будет выводиться сообщение Executing /a, Executing /b или Executing /c. Во всех остальных случаях отобразится ошибка 404 Not Found.
Простой маршрутизатор
Сначала идет определение типа router без полей, который будет использован в реализации интерфейса http.Handler. Для этого нужно определить метод ServerHTTP(). Он использует для URL-запроса инструкцию switch, выполняя различную логику в зависимости от пути. В нем применяется предустановленный ответ 404 Not Found. В main() мы создаем новый router и передаем соответствующий ему указатель в http.ListenAndServe().
Давайте взглянем на это в ole-терминале:
Все работает, как ожидалось. Программа возвращает сообщение Executing /a для URL, который содержит путь /a. При этом для несуществующего пути она возвращает ответ 404. Это тривиальный пример, и сторонние маршрутизаторы, которые вам предстоит использовать, будут иметь намного более сложную логику, но теперь основной принцип вам должен быть понятен.
Создание простого промежуточного ПО:
Пора перейти к созданию промежуточного ПО, выступающего в качестве обертки, которая будет выполняться для всех входящих запросов независимо от целевой функции. В примере из кода ниже мы создаем логер, отображающий время начала и окончания обработки.
Простое промежуточное ПО
По сути, здесь создается внешний обработчик, который при каждом запросе логирует определенную информацию на сервер и вызывает функцию hello(), вокруг которой логика этого процесса и обертывается.
Как и в примере с маршрутизатором, здесь определяется новый тип logger, но на этот раз в нем есть поле inner, которое является самим http.Handler. В определении ServeHTTP() мы используем log() для вывода времени начала и завершения запроса, вызывая между этими выводами метод ServeHTTP() внутреннего обработчика. Для клиента данный запрос завершится внутри этого обработчика. В main() с помощью http.HandlerFunc() из функции создается http.Handler. Здесь реализуется logger, в котором для inner устанавливается только что созданный обработчик. В завершение происходит запуск сервера с помощью указателя на экземпляр logger.
Выполнение кода и отправка запроса выводят два сообщения, содержащих время его начала и завершения:
Маршрутизация с помощью пакета gorilla/mux
Как показано в Простом Маршрутизаторе, с помощью маршрутизации можно сопоставлять путь запроса с функцией. Ее можно использовать также для сопоставления с функцией и других свойств, таких как HTTP-глаголы (методы запроса) или заголовки хостов. В экосистеме Go доступны несколько сторонних маршрутизаторов. Здесь мы представим один из них — пакет gorilla/mux. Но как и в остальных случаях, рекомендуем расширять знания самостоятельно, изучая и другие пакеты по мере их появления на вашем пути.
gorilla/mux — это зрелый сторонний пакет маршрутизации, который позволяет выполнять перенаправление на основе как простых, так и сложных шаблонов. Помимо прочих возможностей, он предоставляет регулярные выражения, вторичную маршрутизацию, а также сопоставление параметров и глаголов.
Рассмотрим пару вариантов применения этого маршрутизатора. Выполнять эти примеры необязательно, так как вскоре мы задействуем их в реальной программе.
Для использования gorilla/mux нужно его сначала установить с помощью команды go get:
Теперь можно приступить к делу и создать маршрутизатор с помощью mux.NewRouter():
Возвращаемый тип реализует http.Handler, а также имеет множество других ассоциированных методов. Например, если требуется определить новый маршрут для обработки запросов GET к шаблону /foo, можно сделать так:
Теперь благодаря вызову Methods() этому маршруту будут соответствовать только запросы GET. Все остальные методы будут возвращать ответ 404. Поверх этого можно надстроить цепочку других квалификаторов, например Host(string), который сопоставляет определенное значение заголовка хоста. Как вариант, следующий код будет сопоставлять только те запросы, чей заголовок установлен как www.foo.com:
Иногда это полезно для сравнения и передачи параметров внутри пути запроса, например при реализации RESTful API. С помощью gorilla/mux это делается легко. Следующий код будет выводить на экран все, что следует за /users/ в пути запроса:
В определении пути параметр запроса задается с использованием фигурных скобок. Можете рассматривать его как место для подстановки. Затем внутри функции-обработчика происходит вызов mux.Vars(), куда передается объект запроса. В ответ вернется map[string] string — карта имен параметров запроса с соответствующими значениями. Поле для подстановки имени user передается в качестве ключа. В итоге запрос к /users/bob должен выдать приветствие для Боба:
Этот шаг можно продолжить, и использовать регулярное выражение для уточнения переданных шаблонов. Например, можно указать, что параметр user должен состоять из букв нижнего регистра:
Теперь любые запросы, не совпадающие с этим шаблоном, будут возвращать ответ 404:
Далее мы разовьем тему маршрутизации, включив реализации промежуточного ПО с помощью других библиотек. Это повысит гибкость обработки HTTP-запросов
Создание промежуточного ПО с помощью Negroni
Простое промежуточное ПО, которое мы показали ранее, логировало время начала и завершения обработки запроса и возвращало ответ. Подобные промежуточные программы не обязательно должны работать с каждым входящим запросом, но в большинстве случаев именно так и будет. Для их применения есть много причин, включая логирование запросов, аутентификацию и авторизацию пользователей, а также отображение ресурсов.
Например, можно написать такую программу для выполнения базовой аутентификации. Она будет парсить заголовок авторизации для каждого запроса, проверять переданные имя пользователя и пароль, возвращая ответ 401 в случае ошибки при аутентификации. Помимо этого, можно связывать в цепочку несколько промежуточных функций так, чтобы они выполнялись поочередно.
При создании промежуточной программы логирования ранее в этой главе мы обернули только одну функцию. На практике же это не особо эффективно, так как вам наверняка понадобится использовать более одной функции. Для этого необходимо применить логику, которая сможет выполнять эти функции поочередно. Написание такого кода с чистого листа не представляет особой сложности, но в этот раз мы обойдемся без изобретения колеса и просто применим проработанный пакет negroni, который уже умеет это делать.
Этот пакет, расположенный в репозитории по адресу https://github.com/urfave/negroni/, хорош тем, что не привязывает вас к крупному фреймворку. При этом его можно легко подключать к другим библиотекам, что делает его особенно гибким.
Также в нем присутствуют предустановленные промежуточные программы, которые могут пригодиться во многих сценариях. Для начала опять же нужно выполнить команду
go get negroni:
Несмотря на то что технически этот пакет можно использовать для всей логики приложения, это будет далеко не самым оптимальным решением, потому что он призван служить промежуточным ПО и не включает маршрутизатор. Лучше применять negroni в тандеме с другим пакетом, например gorilla/mux или net/http. Применим первый для создания программы, которая познакомит вас с negroni и наглядно покажет порядок операций по ходу их реализации в цепочке промежуточных программ.
Начнем с создания нового файла main.go в пространстве имен каталогов, например github.com/blackhat-go/bhg/ch-4/negroni_example/. (Если вы клонировали репозиторий BHG, это пространство имен уже будет создано.) Теперь нужно добавить в созданный файл в код который мы сейчас напишем.
Пример использования Negroni
Сначала, как и ранее, с помощью вызова mux.NewRouter() создается маршрутизатор. Далее идет первое взаимодействие с пакетом negroni, а именно вызов negroni.Classic(). Таким образом создается новый указатель на экземпляр Negroni.
Это можно сделать разными способами: использовать negroni.Classic() или вызвать negroniNew(). Первый вариант, negroni.Classic(), устанавливает набор промежуточных программ по умолчанию, включая логер запросов, утилиту восстановления, которая будет осуществлять прерывание и восстановление в случае паники (аварийной остановки выполнения программы), а также программу, которая будет предоставлять файлы из публичного каталога, расположенного в той же папке. Что же касается функции negroni.New(), то она не создает предустановленного промежуточного ПО.
В пакете negroni доступна каждая из перечисленных промежуточных программ. Например, пакет восстановления можно добавить, выполнив
Далее следует добавление в стек промежуточного ПО маршрутизатора с помощью вызова n.UseHandler(r). Планируя и собирая собственный промежуточный комплект программ, не забудьте учесть порядок их выполнения. Например, необходимо, чтобы программа проверки аутентификации срабатывала до функции-обработчика, которая эту аутентификацию требует. Любая такая программа, надстроенная над маршрутизатором, будет выполняться после обработчика. Порядок важен. В данном случае мы не определяли собственное ПО, но вскоре к этому прибегнем.
Сейчас же мы создадим сервер из Примера использования Negroni и запустим его. Затем отправим ему веб-запросы по адресу http://localhost:8000. В результате программа логирования negroni должна вывести информацию в stdout, как показано далее. В выводе отражены временная метка, код ответа, время обработки, хост и HTTP-метод:
Конечно, предустановленное промежуточное ПО — это очень хорошо, но реальная мощь проявляется, когда вы создаете собственное. При работе с negroni добавлять промежуточные программы в стек можно с помощью нескольких методов. Взгляните на следующий код. Он создает простую программу, которая выводит сообщение и передает выполнение следующей программе в цепочке:
Эта реализация немного отличается от предыдущих примеров. Ранее мы реализовывали интерфейс http.Handler, который ожидал метод ServeHTTP(), получающий два параметра: http.ResponseWriter и *http.Request. В этом же примере вместо интерфейса http.Handler реализуем интерфейс negroni.Handler.
Небольшое различие здесь в том, что интерфейс negroni.Handler ожидает реализации метода ServeHTTP(), который получает уже не два, а три параметра: http.ResponseWriter, *http.Request и http.HandlerFunc. Параметр http.HandlerFunc представляет следующую промежуточную функцию в цепочке, которую мы назовем next. Сначала обработка выполняется методом ServeHTTP(), после чего происходит вызов next(), которой передаются изначально полученные значения http.ResponseWriter и *http.Request. В результате выполнение передается дальше по цепочке.
Но нам по-прежнему нужно указать negroni использовать в цепочке промежуточного ПО и нашу реализацию. Для этого можно вызвать метод negroni под названием Use и передать ему экземпляр реализации negroni.Handler:
Писать собственный набор промежуточных программ с помощью этого метода удобно, поскольку можно легко передавать их выполнение по цепочке. Но при этом есть один недостаток: все, что вы пишете, должно использовать negroni. Например, если создать пакет промежуточного ПО, который записывает в ответ заголовки безопасности, то он должен будет реализовывать http.Handler, чтобы его можно было применять и в других стеках приложения, так как большинство из них не будут ожидать negroni.Handler. Суть в том, что независимо от назначения создаваемых промежуточных программ проблемы совместимости могут возникнуть при попытке использовать промежуточное ПО negroni в другом стеке и наоборот.
Есть два других способа сообщить negroni, что следует задействовать ваше промежуточное ПО. Первый из них — это уже знакомый вам UseHandler (handler http.Handler). Второй — это вызов UseHandleFunc(handlerFunc func(w http.ResponseWriter, r *http.Request)). Последним вы вряд ли станете пользоваться часто, поскольку он не позволяет поочередно выполнять программы в цепочке. Например, если нужно написать промежуточную функцию для выполнения аутентификации, то в случае неверной информации сессии или учетных данных потребуется возвращать ответ 401 и останавливать выполнение. С помощью названного метода это сделать не получится.
Добавление аутентификации с помощью Negroni:
Прежде чем продолжать, давайте изменим пример из предыдущего раздела, чтобы продемонстрировать использование context, который может легко передавать переменные между функциями. В примере из кода ниже с помощью negroni добавляется промежуточная программа аутентификации.
Использование context в обработчиках
Здесь мы добавили новую промежуточную программу, badAuth, которая будет симулировать аутентификацию исключительно в целях демонстрации. Этот новый тип содержит поля Username и Password и реализует negroni.Handler, поскольку в нем определяется версия метода ServeHTTP() с тремя параметрами. Внутри ServeHTTP() сначала из запроса извлекаются имя пользователя и пароль, после чего их значения сравниваются с имеющимися полями. Если данные не совпадают, выполнение останавливается и запрашивающей стороне отправляется ответ 401.
Обратите внимание на то, что мы делаем возврат до вызова next(). Это останавливает выполнение оставшейся цепочки промежуточных программ. Если учетные данные окажутся верными, выполняется довольно объемный код для добавления имени пользователя в контекст запроса. Сначала происходит вызов context.WithValue() для инициализации контекста из запроса с установкой в него переменной username. Затем мы убеждаемся, что запрос использует новый контекст, вызывая r.WithContext(ctx). Если вы планируете написать веб-приложение на Go, то вам нужно будет получше познакомиться с этим шаблоном, поскольку применять его придется часто.
В функции hello() мы получаем имя пользователя из контекста запроса, применяя функцию Context().Value(interface{}), которая возвращает interface{}. Так как вам известно, что это строка, здесь можно задействовать утверждение типа. Если же вы не можете гарантировать тип или то, что это значение будет существовать в этом контексте, используйте для преобразования инструкцию switch.
Выполните сборку и запустите код Использования context в обработчиках, а затем отправьте несколько запросов на сервер. Попробуйте использовать как верные, так и неверные учетные данные. Вывод должен получиться следующим:
Отправка запросов без учетных данных приводит к возврату ошибки 401 Unauthorized. Если тот же запрос отправить с верным набором данных, то в ответ придет суперсекретное сообщение, доступное только аутентифицированным пользователям.
Усвоить нужно очень большой объем рассмотренного здесь материала. Функции-обработчики используют для записи ответа в экземпляр http.ResponseWriter только fmt.FPrintf(). Закончу эту статью последним разделом про создание HTML- ответов с помощью шаблонов.
Создание HTML-ответов с помощью шаблонов:
Шаблоны позволяют динамически генерировать содержимое, включая HTML, с помощью переменных из программ Go. Во многих языках генерация шаблонов реализуется с помощью сторонних пакетов. В Go для этой цели есть два пакета, text/template и html/template. Мы же в этой главе используем пакет HTML, потому что он предоставляет необходимую нам контекстную кодировку.
Одна из особенностей учета контекста в пакете Go заключается в том, что он кодирует переменную по-разному, в зависимости от ее расположения в шаблоне. Например, строка в виде URL, переданная в атрибут href, будет закодирована в URL, но при отображении в HTML-элементе она будет закодирована уже в HTML.
Процесс генерации шаблона для его дальнейшего использования начинается с определения самого шаблона, который содержит поле ввода для обозначения динамических контекстных данных для отображения. Его синтаксис покажется знакомым тем, кто применял Jinja совместно с Python. При отрисовке шаблона мы передаем ему переменную, которая будет использоваться в качестве контекста. Она может быть сложной структурой с несколькими полями либо примитивом.
Давайте проработаем код ниже, который создает простой шаблон и заполняет поле ввода JS-кодом. Это искусственный пример, показывающий, как динамически заполнять содержимое, которое возвращается в браузер.
HTML-шаблонизация
Сначала создается переменная x, которая будет хранить HTML-шаблон. Здесьдля определения шаблона используется строка, вложенная в код, но в большинстве случаев вам потребуется хранить шаблоны в отдельных файлах. Обратите внимание на то, что этот шаблон представляет простую HTML-страницу. Внутри него с помощью специальной инструкции {{variable-name}} определяются поля ввода. Variable-name — это элемент внутри контекстных данных, который требуется отобразить. Напомним, что это может быть структура или другой примитив.В данном случае мы используем одну точку, сообщая таким образом пакету, что нужно отобразить весь контекст. Учитывая, что мы будем работать с одной строкой, это нормально, но в случае применения более крупных и сложных структур, таких как struct, получение нужных полей осуществляется вызовом после этой точки. Например, если в шаблон передать структуру с полем Username, то отобразить это поле можно будет, используя выражение {{.Username}}.
Далее в функции main() с помощью вызова template.New(string) создается новый шаблон ❸. Затем выполняется вызов Parse(string), обеспечивающий верное форматирование шаблона и его парсинг. Совместно эти две функции возвращают указатель на Template.
В этом примере задействуется всего один шаблон, но шаблоны можно вкладывать в другие шаблоны. При использовании нескольких шаблонов для удобства их дальнейшего вызова важно именовать их последовательно. В завершение происходит вызов Execute(io.Writer, interface{}), который обрабатывает шаблон, используя переменную, переданную в качестве второго аргумента, и записывает его в предоставленный io.Writer. Для демонстрации мы применяем os.Stdout. Вторая передаваемая в метод Execute() переменная — это контекст, который будет использоваться для отображения шаблона.
Выполнение этого кода сформирует HTML-код, и можно заметить, что теги скриптов и другие переданные в контексте вредоносные символы закодированы правильно:
О шаблонах можно сказать еще много, например то, что вместе с ними допустимо применять логические операторы или что их можно задействовать с циклами и другими управляющими конструкциями. Помимо этого, они позволяют использовать встроенные функции и даже определять и раскрывать любые вспомогательные функции, что существенно расширяет возможности шаблонизации. Я советую вам познакомиться со всеми этими возможностями получше погуглив.
Dio vs HTTP: опыт перехода от одного к другому
Меня зовут Влад, я iOS/Flutter-разработчик в DexSys на проекте DexBee*. В этой статье:
• расскажу о своём опыте перехода с одного HTTP клиента(HTTP) на другой(Dio)
• объясню, для чего это было сделано, и какие преимущества имеет Dio над HTTP
• кратко расскажу про кодогенератор Freezed и потоки Flutter’а. Конечно, с примерами кода. А в конце статьи вы найдете демо-проект под всё это дело)
*DexBee - это клубная система вовлечения клиентов в занятия фитнесом. В основе системы лежит контроль нагрузки клиента во время тренировки.
«DexBee Клуб» - приложение для управления настройками клуба в системе. Первый релиз приложения был в конце 2021 года, и работа над ним до сих пор продолжается. Приложение написано на Flutter.
Почему понадобился переезд?
Причина простая – легаси. Нам надо было сделать MVP продукта, проверить насколько он будет полезен клиентам, и от этого уже отталкиваться, принимая решение - есть ли необходимость в нашем продукте.
MVP – проект с минимальным функционалом, который покрывает некоторые потребности потребителя.
Поэтому решили попробовать кроссплатформу. На тот момент я писал только под iOS, и знания по Flutter у меня были нулевыми. Для флаттера на тот момент был выпущен уже второй мажорный релиз. Язык развивался, да и сейчас развивается, как на дрожжах, количество вакансий для него росло, сообщество разработчиков под него в России тоже немалое, что наталкивает на мысли о хорошем будущем фреймворка.
Так вот, мне дали 2-3 недельки на изучение и ещё немного времени на простенький демо-проект, чтобы проверить, как это вообще работает, и получится ли что-то дельное. Далее мы запустили первую версию Dexbee Клуб, в которой можно было авторизоваться и протестировать интернет на доступ к нашим сервисам. Этот функционал, в первую очередь, помогал нашей бравой техподдержке подсказывать клиентам как правильно разместить оборудование в клубе.
Здесь же хотел бы выразить огромную благодарность этим крутым ребятам, которые держат рубеж наплыва отзывов от клиентов, как хороших, так и плохих, не давая тем самым разрушиться моему хрупкому мирочку)
Нужно было создать два запроса на сервер – авторизацию и обновление логин-токена. Перечитав кучу статей и форумов с вопросом «А что выбрать-то?», я чаще всего получал ответы как раз про HTTP и Dio. Единогласного ответа, конечно же, не получить, но большинство рекомендовало именно эти пакеты. Я решил взять HTTP, он показался простеньким для изучения и справлялся с основными функциями, что и сейчас прекрасно делает.
Дальше мы собрали первые отзывы по приложению и решили развивать. Клиент серверных запросов жирел и матерел с каждым новым релизом. Возможно, я чуть-чуть преувеличиваю, но дополнять его с каждым новым запросом становилось всё труднее и труднее(из-за лени).
Тема для холивара: проектировать сразу хорошо или всё-таки ради скорости можно поступиться качеством?
Для проверки теории, а может из-за опыта написания на Flutter, я решил поступиться качеством и спроектировать, так сказать, не самое «резиновое» решение, но быстро внедряемое.
Проект нуждался в доработках клиент-серверной части, а в нашей команде есть возможность облегчить себе жизнь, если это несет плюсы для всего проекта: разработчик описывает, что хочет улучшить, какие выгоды от этого получит проект, сколько времени потребуется, и что тестировщику придётся проверять в итоге. Задача уходит руководителю, а он, в зависимости от степени важности исправлений, заносит её в один из будущих релизов.
Преимущества Dio над HTTP
На этот раз я взял в работу Dio, сейчас объясню почему: HTTP умеет в GET, POST, PUT, DELETE, PATCH, загружать файлы с помощью Multipart запроса.
Dio умеет всё тоже самое, но с некоторыми преимуществами:
Глобальные настройки - при создании Dio можно указать baseURL, таймауты соединения, получения ответа и так далее.
Interceptor - это перехватчик, в котором можно дополнить или вставить дополнительные действия перед отправкой запроса, скорректировать его ответ или ошибку. Применить можно для логирования, обновление токена авторизации и так далее.
Есть возможность отменить выполнение всех запросов разом. И отменить отдельно взятый.
Отмечу, для HTTP уже написаны пакеты, которые дополняют его функционал и есть возможность получить все плюшки, какие имеет Dio, но у Dio всё это есть «из коробки». Важно понимать, оба пакета работают с одинаковой скоростью и быстрее Dio отправлять/получать ответ от сервера не будет.
Кажется, будто бы HTTP – голый клиент, Dio – уже нарощенный дополнительными плюшками, но, как оказалось позже, есть ещё Chopper и Retrofit – кодогенераторы запросов. Для создания запроса надо описать сигнатуру метода: путь, параметры урла, боди, что должно вернуться в ответе, запустить генератор, и тело метода создастся. Ускорение ускорения или ещё один из этапов программистской лени?
Freezed
«yet another code generator» – как пишут создатели пакета. Я бы лучше назвал подобные пакеты «инструкцией для генерации», то есть генерирует код пакет build_runner, а во Freezed описано, как надо сгенерировать. Если вкратце, с помощью него можно описать структуру серверного ответа, запустить генерацию кода и, «вуаля», парсер готов. Описанный мной функционал лишь минимальная часть того, что умеет этот пакет, так что, если вы ещё не ознакомились с ним, то я рекомендую перейти и почитать подробнее здесь. Модель дополнится методами copyWith и toString. Всё это необходимо и, порой, занимает кучу монотонного времени.
Потоки Flutter
При запуске Flutter-приложения вызывается метод main, в котором создается главный изолят, т.е. главный поток или поток для отрисовки интерфейса. После main метода запускается цикл событий, Event Loop.
Event Loop, как и следует из названия, это цикл, конец которого зависит от жизни потока, в котором он работает, в данном случае цикл завершится, как только приложение будет выгружено из памяти.
В нём происходит выполнение очередей по принципу FIFO(First in - First out). Как следует из названия, хоть тут и имеется возможность создать асинхронные Future методы – они не выполняются синхронно. Очереди делятся на два типа:
Microtask – служит для выполнения задач, не занимающих много времени. Создать можно при помощи Future.microtask.
Event – все остальные операции. От взаимодействия с UI до работы с БД, API и т.д.
Очереди выполняются именно в такой последовательности. Сначала массив microtask, а далее events. После выполнения любой инструкции, будет запущена очередь microtasks. По работе Event Loop есть отличная видеопрезентация, смотрите тут.
Для выполнения реального синхронного кода есть две возможности: метод compute и, более сложное для реализации, использование класса Isolate. Оба варианта создают новый поток или, по-другому, – изолят. Назван он так не случайно. Каждый поток во Flutter изолирован от других и имеет свою собственную область памяти. Нет возможности допустить deadlock, и это круто. Но, чтобы воспользоваться объектами, созданными одним изолятом, надо передать их копию в другой изолят. Общение происходит через сообщения, поэтому оба изолята должны знать порты друг друга. Изоляты следует уничтожать, чтобы они не занимали память впустую .
Передать новому изоляту можно только функцию верхнего уровня, глобальную функцию, или статический метод класса.
Метод compute настраивает общение и уничтожение изолята внутри себя.
Вот отличная статья, иллюстрирующая, почему стоит пользоваться изолятами: https://dev.to/alphamikle/why-should-you-use-isolates-in-flu...
И ещё список для статей для более глубокого понимания работы изолятов:
• https://habr.com/ru/articles/497278/#
• https://blog.codemagic.io/understanding-flutter-isolates/
• https://martin-robert-fink.medium.com/dart-is-indeed-multi-t...
• https://dart.dev/language/concurrency
• https://skondratev.com/futures-isolates-event-loop-vo-flutter/#:~:text=Модель исполнения в Dart,Дарта — Изолят ( Isolate ).
Пример
В этой секции я напишу простенькое клиент-серверное приложение с использованием Dio, покажу, как добавить в проект кодогенератор freezed и как им воспользоваться, и выведу отправку запроса и декодирование JSON в отдельный изолят. В свободном доступе есть куча бесплатных API, на которых можно попрактиковаться, найти их можно здесь. Я выбрал Spaceflight News API, а метод v3/articles с загрузкой 100000 элементов за один запрос.
Итак, чтобы добавить Dio в проект, надо написать название пакета в pubspec.yaml и выполнить команду pub get.
Дабы не писать под каждый запрос отдельный метод, я описал класс NetworkRequest, в котором будут храниться необходимые параметры запроса.
Чтобы ответ не сломался из-за нескольких неожиданных элементов, имеющих не тот тип, который необходим нам, я сразу же описал класс ответа на запрос списка «космических» новостей. Для этого обернул конструктор ArticleResponse в конструкцию try catch.
Теперь и сам NetworkManager. При инициализации менеджера, создал клиент с настройками по умолчанию.
Метод performRequest принимает параметры запроса и функцию, по которой будет парсить ответ, если запрос успешен. Model - это тот объект, что мы ожидаем на выходе.
Вот и пример его применения.
В текущем виде запрос выполняется в главном изоляте, что, при развитии проекта, может мешать отрисовке интерфейса. Да и с добавлением новых запросов придётся описывать всё больше ответов с сервера. Не вручную же это делать. Так что, первым делом добавлю пакет freezed в проект. Делается это при помощи всё того же pubspec.yaml.
dependicies – пакеты с фреймворками и библиотеками, которые используются в проекте, например, элементы интерфейса или утилиты для использования возможностей телефона, локатора, блютуз и т.д.
dev-dependicies – пакеты, помогающие в разработке проекта, например: тесты, кодогенераторы и т.д.
Выполнить всё тот же pub get, и поехали пользоваться.
Все подчеркивания это нормально. Теперь необходимо запустить команду генерации. Делается это следующим образом: надо открыть Терминал, перейти в папку с проектом и выполнить команду flutter pub run build_runner build. После успешного выполнения команды подчеркивания должны пропасть. Вот таким нехитрым образом и дополнилась модель респонса.
Далее, я выведу выполнение каждого запроса в отдельный изолят, воспользовавшись функцией compute. Созданный изолят не может получить доступ к уже настроенному Dio клиенту, поэтому я создам класс, в котором будет храниться клиент, запрос и функции для парсера.
И опишу глобальный метод, который нужно будет выполнить.
Таким образом, в performRequest я создам IsolatedNetworkRequest и выполню метод compute.
Теперь каждый запрос будет выполняться в отдельном изоляте и не мешать работе главного.
Итог
У нас есть клиент с глобальными настройками, для которого можно написать перехватчики, если появится необходимость корректировать отправку и результаты запросов. На каждый новый ответ нет необходимости тратить время на описание класса. А запросы не мешают работе изолята, отвечающего за отрисовку интерфейса. Ознакомиться с демо проектом можно по этой ссылке: https://github.com/Wenomok/dio_example
Буду рад любым отзывам! Есть желание и дальше писать статьи и делать их качественными и максимально интересными.
Автор: Влад, iOS/Flutter-разработчик в DexSys
Https, http и ssl
Конкурс для мемоделов: с вас мем — с нас приз
Конкурс мемов объявляется открытым!
Выкручивайте остроумие на максимум и придумайте надпись для стикера из шаблонов ниже. Лучшие идеи войдут в стикерпак, а их авторы получат полугодовую подписку на сервис «Пакет».
Кто сделал и отправил мемас на конкурс — молодец! Результаты конкурса мы объявим уже 3 мая, поделимся лучшими шутками по мнению жюри и ссылкой на стикерпак в телеграме. Полные правила конкурса.
А пока предлагаем посмотреть видео, из которых мы сделали шаблоны для мемов. В главной роли Валентин Выгодный и «Пакет» от Х5 — сервис для выгодных покупок в «Пятёрочке» и «Перекрёстке».
Реклама ООО «Корпоративный центр ИКС 5», ИНН: 7728632689