Что такое REST API? HTTP, Клиент-Сервер, Проектирование, Разработка, Документация, Swagger и OpenApi

ruticker 04.03.2025 23:48:01

Текст распознан YouScriptor с канала Максим Иглин

распознано с видео на ютубе сервисом YouScriptor.com, читайте дальше по ссылке Что такое REST API? HTTP, Клиент-Сервер, Проектирование, Разработка, Документация, Swagger и OpenApi

Привет! Давайте сегодня попробуем разобраться с самым популярным способом взаимодействия веб-приложений — **REST API**. Пройдемся по всей теоретической базе, разберемся с принципом работы и научимся проектировать, а также рассмотрим наиболее частые случаи. Если вы уже знакомы с API, всё равно досмотрите это видео до конца. Возможно, вы найдёте что-то новое и конкретное для себя. Для того чтобы полностью усвоить данный материал, необходимо повторить базу про HTTP. Выжимку, по которой вы найдете в моем Telegram-канале, ссылка на который есть в описании. Кроме того, вы можете присоединиться к моему чату в Telegram и поделиться своим опытом с другими разработчиками. Начнем с базы: **REST** — это архитектурный стиль взаимодействия распределенных систем, будь то фронтенд и бэкенд приложения или несколько бэкенд приложений, интегрированных друг с другом. Давайте в дальнейшем будем рассматривать на классическом примере: у нас есть фронтенд приложение и бэкенд приложение, одно из которых является клиентом, а второе — поставщиком данных. В терминологии REST называются ресурсами. Задача сервера — собирать, обрабатывать и отдавать клиенту эти самые ресурсы. Итак, что нам нужно для успешного взаимодействия этих двух систем и построения REST API? 1. **Адрес**. Откуда мы будем забирать или куда будем доставлять наши данные? Представим, что вы зашли в библиотеку, в которой сидит библиотекарь. Вы в данном случае являетесь клиентским приложением, а библиотекарь — серверным, который имеет доступ к некоторым ресурсам, то есть книгам. Чтобы узнать, какие книги есть в библиотеке, вам необходимо знать, где находится сам библиотекарь, и назвать ему автора. То есть вы готовы сформировать запрос по конкретному адресу. Этот адрес должен быть уникальным и гарантировать нам, что именно по нему мы получим те данные, которые нам нужны. В случае с API уникальный адрес ресурса называется **URI**. Важно не путать его с **URL**. URL — это адрес, который указывает на местоположение ресурсов в сети, его называют **endpoint**. Например, у нас есть адрес, по которому мы получаем список книг: `libraryan.com/books`. Явный пример URL — и тот же адрес, но с фильтрацией книг по автору: `libraryan.com/books?author=Гоголь`. То есть это конкретный пример URI, URL с набором параметров — это URI. 2. **Способ получения ресурсов**. В случае с API это протокол HTTP с набором его базовых методов: **GET**, **POST**, **PUT**, **PATCH** и **DELETE**. Вернемся к библиотекарю: чтобы получить информацию о том, какие книги конкретного автора есть в библиотеке, мы подходим к библиотекарю и называем ему автора, то есть отправляем запрос на конкретный URI. Этот процесс крайне схож с работой HTTP метода **GET**, по которому мы получаем наши ресурсы. Метод GET — это наша просьба «Расскажи», с помощью которого наше клиентское приложение запрашивает ресурсы у серверного. Если вам нужно украсть Гоголя, вы придете с запросом «Принеси, я заберу и не верну». Это аналогично работе HTTP метода **DELETE**. 3. **Обратная связь от сервера**. Она позволяет понять статус обработки запроса. Для этого в REST API используется стандартные HTTP статус-коды. Каждый статус-код имеет свое числовое значение и смысл, который позволяет клиентскому приложению понять, как дальше обрабатывать то, что он получил. Например, при обращении к библиотеке для получения списка книг несуществующего автора библиотекарь скажет нам, что такого автора нет и книг, соответственно, этого автора нет. Это аналогично HTTP response со статус-кодом **404 Not Found**. То есть сервер сказал, что такого ресурса нет, и клиент на основе этого что-то предпринял. О статус-кодах мы также поговорим, когда рассмотрим проектирование REST API. Давайте рассмотрим еще один важный момент: как же мы передаем данные с клиента на сервер и обратно? 1. **GET параметры и параметры пути**, которые формируют URI. 2. **Тело запроса**, чаще всего в виде JSON. Для отправки данных на сервер нам важен формат данных, который будут понимать почти все языки программирования, все клиентские приложения и все серверные приложения. Это формат JSON. Процесс перевода данных из формата, который понимает серверное приложение (например, какие-нибудь словарики в Python), в формат, понятный всем приложениям, называется **сериализацией данных**. Обратно ему процесс называется **десериализацией**. Также мы можем передавать данные в заголовках и в куках. Заголовки и куки чаще всего используются для авторизации. Что нам может отдать сервер? Прежде всего, это данные, формат которых определяется заголовком **Content-Type**. Чаще всего это `application/json`, статус-код ответа и заголовки и куки, которые устанавливает сам сервер. Чтобы посмотреть, например, такого запроса, достаточно открыть любое современное **Single Page Application**, написанное, например, на React. Открыть Chrome DevTools, дальше вкладочку **Network**, поставить фильтр **XHR**. Здесь мы увидим AJAX запросы на сервер, скорее всего, это будут REST API, по которым фронтенд получает данные с сервера. Важно помнить, что сервер не хранит информацию о предыдущих запросах. То есть это такой библиотекарь с деменцией: вы у него один раз спросили, на следующий раз он уже не помнит, что вы спрашивали до этого. Именно поэтому REST называют **stateless**, то есть не имеющим состояния. Важно понимать, что сколько бы слоев у нас ни было на стороне бэкенд приложения (то есть различных интеграций, обращений во внешние системы и прочее), клиент всегда обращается именно к внешнему слою и стучит именно в него. При этом ему абсолютно всё равно, что происходит под капотом, лишь бы вернулись необходимые ресурсы. Кроме того, REST поддерживает **кэширование**, которое играет важную роль для повышения производительности, снижения нагрузки на сервер и сокращения задержек передачи данных. Кэширование позволяет сохранять ранее полученные данные и использовать их повторно вместо повторной отправки запроса на сервер. Сервер может добавить заголовки HTTP ответа, такие как **Cache-Control**, **Expires** и **ETag**, чтобы управлять кэшированием на стороне клиента. Использование этих заголовков позволяет указывать клиентам, как долго данные могут быть закэшированы и как проверять актуальность закэшированных данных. Клиентские приложения могут использовать локальное хранилище, такое как **Local Storage** или **IndexedDB**, для сохранения закэшированных данных на стороне клиента. Когда клиент делает запрос, приложение сначала проверяет, есть ли данные в локальном хранилище, и если они присутствуют, использует их вместо отправки запроса на сервер. Давайте подрезюмируем: фронтенду нужны данные для отображения ресурсов. Он делает HTTP-запрос с определенным методом на бэкенд по определенному URI. Бэкенд готовит и сериализует данные, затем отдает HTTP response клиенту с ресурсами и статус-кодом, на основе которого фронтенд отображает данные или, например, выводит сообщение об ошибке. Давайте перейдем к проектированию API. Сначала рассмотрим процессную составляющую, а потом техническую. Проектирование всегда начинается с составленного бизнес-флоу и дизайн-проекта. То есть нам нужно заранее понимать, как клиент и сервер будут отображать, хранить и обрабатывать данные. В нашем дизайн-проекте, например, есть список книг, которые фронтенд должен отобразить. Книги хранятся в базе данных, фронтенд делает запрос на бэкенд на определенный REST API метод по определенному URI. Бэкенд, в свою очередь, идет в базу данных за книгами, сериализует их и отдает HTTP response обратно. Фронтенд берет и отображает. На данном этапе нам важно понимать, какие данные и как нам следует перегонять. Затем происходит процесс написания спецификации к API методу. То есть фиксируются все входные и выходные параметры и URL, по которому наш API метод будет доступен. Этим занимается, если в команде есть системный аналитик, то это его задача. Аналитик формирует предварительные ТЗ и согласует с разработчиками, чтобы удовлетворить обе стороны: клиентскую и серверную. Для того чтобы это было без проблем реализуемо. Если разработчиков удовлетворили требования, то они приступают к разработке. Окей, а что если у нас в команде нет системного аналитика? В данном случае проектированием REST API занимаются тимлиды разработки либо те разработчики, которые будут непосредственно это делать. Теперь давайте подробно про принципы проектирования. Рассматривать мы будем на классическом примере: у нас есть реляционная БД, в которой есть несколько табличек. У нас есть табличка **books**, которая имеет поля **ID** и **title**, дальше табличка **authors**, которая имеет поля **ID** и **name**, табличка **author_books**, которая имеет ссылку на автора и ссылку на книгу, то есть табличка для связи **Many-to-Many**. Чтобы показать, что у одной книги может быть несколько авторов, и у одного автора может быть несколько книг, и табличка **pages**, которая связана с книгами связью **One-to-Many**. Она имеет поля **number**, **book_id** и **content**. Мы всегда начинаем со структуры URL для доступа к ресурсам. Итак, для начала нам нужно дать возможность версионировать наши методы. Например, мы сделали какой-нибудь метод: `libraryan.com/api/v1/books`. В данном случае префикс **v1** показывает на версию этого метода. Затем нам нужно что-то отрефакторить, поменять логику, но нельзя отключать предыдущий метод, потому что им пользуются какие-то клиенты. Поэтому мы делаем метод с версией **v2**, то есть префикс наш меняется с **v1** на **v2**. Именно этот процесс называется **версионированием API**, которое должно поддерживать любое бэкенд-приложение. Начнем с endpoint для книг. Первое правило именования URL: это один endpoint — одна сущность, которая отражена в endpoint во множественном числе. То есть все операции с книгами мы будем осуществлять по URL: `libraryan.com/api/v1/books`. Видим, что в endpoint есть название ресурса, который мы будем обрабатывать, оно во множественном числе. Какие действия мы можем совершить с книгами, не привязываясь к остальным сущностям (то есть страницам, авторам и прочим)? Первое — это получить список всех книг или получить список одной книги. Изменить название книги, удалить книгу. Давайте спроектируем эти методы. При проектировании учитываем три основных составляющих: 1. **GET параметры**, тело запроса и заголовки. 2. **Способ авторизации**. В нашей спецификации всегда должен быть указан способ авторизации клиента на сервере, чтобы проверить, есть ли у этого клиента доступ к этим ресурсам. 3. **Тело ответа** при возможных статус-кодах. То есть у нас может быть статус-код **200**, одно тело ответа, и статус-код **400** — тело ответа уже другое. Начнем с метода **GET** для получения списка книг. Endpoint в данном случае будет `api/v1/books`. Входные параметры: во входных параметрах мы указываем название параметра, его тип, местоположение и описание. В данном случае у нас один входной — это заголовок **Authorization**, для того чтобы передавать в нем JWT токен для авторизации. Далее описываем ответ. Первое — это статус-код. Статус-код **200 OK** говорит нам о том, что сервер принял запрос, успешно его отработал и отдал необходимые нам ресурсы. Тип содержимого **Content-Type** в данном случае будет `application/json`, и пример ответа. Спецификацию желательно привести с примером ответа, чтобы обеим сторонам было легче, и документация нашего API была более полной. В данном случае это JSON, который имеет в себе массив объектов с полями **id** и **title**. Далее мы перечисляем другие статус-коды, которые может отдать наш сервер данному методу. Это статус-код **403**, то есть пользователь не прошел авторизацию, у него нет доступа к этим ресурсам, и в теле ответа выводим информацию о том, что пользователь не авторизован, то есть у него не хватает прав. Итак, сейчас мы написали спецификацию к нашему первому API методу, зафиксировали все входные параметры, выходные параметры и возможные ответы при различных статус-кодах. Далее спроектируем метод для получения одной книги. В URI добавляется параметр **ID**. Во входных параметрах у нас по-прежнему **Authorization**, который находится в заголовках, и **ID**, который является параметром пути. Мы знаем, что для того чтобы грамотно спроектировать REST API, нам нужен идентификатор каждого ресурса. Поэтому, чтобы получить конкретную книгу, мы добавили дополнительный параметр **ID**. Это еще одно правило проектирования: поинты сначала название ресурса во множественном числе, то есть `/books`, затем его идентификатор, который является параметром пути **ID**. Это касается всех методов, которые обрабатывают конкретную сущность. То есть это могут быть методы **GET**, **PUT**, **PATCH**, **DELETE** и прочие. Давайте повторим: если мы сделали запрос на `libraryan.com/api/v1/books/2`, в данном случае передастся как идентификатор книги. Статус-код **200** говорит нам о том, что сервер всё принял, всё отдал нам данные, тип содержимого контента по-прежнему `application/json`. Дальше я его проговаривать не буду, и пример ответа вы видите: JSON с одной книжечкой, её идентификатор и название. Далее возможные статус-коды: **403** — с ним мы уже знакомы, и новый статус-код **404 Not Found**. Он нам говорит о том, что сервер принял запрос успешно, его обработал, но не нашел запрашиваемый ресурс. То есть **404** мы используем тогда, когда сервер не нашел ресурс. Давайте повторим: **200** мы используем, когда всё окей, сервер принялся, просто обработал и отдал данные; **403** — это ошибка авторизации, то есть у клиента не хватает полномочий для доступа к ресурсам; и **404** — ресурс не найден. Пример ответа при **404** может быть аналогичен **403**, то есть мы выводим либо **detail**, либо **message** и пишем какое-то сообщение клиенту. Он уже решает, выводить его или обработать так, как ему угодно. Спроектируем метод **POST** для создания книги. Итак, у нас endpoint `libraryan.com/api/v1/books`, и входные параметры здесь уже добавляется тело запроса. Метод **POST** мы используем вместе с телом запроса. Тело запроса, как правило, закидывается в JSON, который летит на обработку на сервер. Итак, с параметрами **Authorization** и **Content-Type** мы уже знакомы, но кроме того, теперь мы передаем тело запроса, в котором есть параметр **title**, который является строкой и это название книги. Пример запроса в данном случае будет JSON с полем **title**, значение которого будет строкой, например, **newbook**. Что сервер даст нам в ответ? Статус-код **201 Created** сообщит нам об успешном создании ресурса, то есть сервер принял наш запрос, отработал и создал ресурс на своей стороне. В ответе он нам обязан отдать идентификатор этого ресурса и сам ресурс. Однако бывают исключения, например, при регистрации, когда мы вводим логин и пароль, возможно, он обратно не вернется. Какие у нас еще могут быть статус-коды? **403** — с ним мы уже знакомы, **400 Bad Request**. **400 Bad Request** говорит нам о том, что мы передали недостаточно данных или некорректные данные для создания ресурса. Или, например, мы отправим запрос в теле которого будет сам **field** **null**, сервер не ожидает такого тела запроса, поэтому он обязан нам вернуть **400 Bad Request**. Давайте спроектируем метод **PATCH** для частичного обновления ресурса. Почему частичного? Существует два метода: **PUT** и **PATCH**. Оба обновляют ресурсы, **PATCH** делает это по конкретным полям частично, **PUT** делает это совсем ресурсом целиком. На практике часто используют **PATCH**, поэтому давайте остановимся на нем. Итак, для того чтобы обновить ресурс, в нашем API появляется параметр пути **ID**, то есть идентификатор ресурса, который мы собираемся обновлять. Входные параметры: **Authorization**, **Content-Type** уже понятно, **ID** как параметр пути, то есть идентификатор ресурса, и **title**, который находится в базе. То есть метод **PATCH** мы будем использовать для того, чтобы изменить название нашей книги. Для этого в теле запроса передаем параметр **title** с новым названием. Что сервер нам вернёт? Успешная обработка **200** и обновленный ресурс в теле ответа. С **403** всё понятно, с **400** я думаю, всё понятно. Давайте повторим: если мы отправим не **title**, а что-то другое, сервер не сможет обновить ресурс, потому что такого поля у него нет. Или, например, тип значения поля, которое мы отправим, не будет удовлетворять типу поля ресурса, сервер тоже нам вернет **400**. И статус-код **404** говорит нам о том, что ресурс с переданным идентификатором не найден, и обновлять нам нечего. Спроектируем метод для удаления конкретной книги. Как мы рассуждаем? Конкретная книга, так значит, нам нужен идентификатор конкретной книги. Хорошо, куда мы его кладем? Сразу после создания ресурса во множественном числе в параметрах пути. Итак, у нас получается метод **DELETE** на `api/v1/books/{id}`. При успешном удалении ресурса сервер возвращает нам **204 No Content**, то есть говорит о том, что удаление прошло успешно и такого ресурса больше нет. Ну или **403** — обида, что мы не можем удалить данный ресурс. Давайте повторим: метод **GET** мы используем для получения ресурса по URI. Частью URI может быть параметр пути, который является идентификатором ресурса, или GET параметры. Кроме того, нам нужно передавать способ авторизации на сервере, он может быть JWT токеном или идентификатором, всё в куках. Если метод **GET** отработал успешно, сервер вернет нам HTTP response со статусом **200 OK**, который показывает, что всё хорошо. Метод **POST** мы используем для создания ресурса на сервере. В HTTP-запросе клиент передает тело запроса с указанным **Content-Type**. Если какая-то сущность создаётся, то сервер отдает HTTP response со статус-кодом **201 Created**. Если POST был просто в качестве передачи данных на сервер, то есть без создания сущности, сервер вернет нам **200 OK**. Если наш запрос не удовлетворяет требованиям, которые ожидает сервер, то он вернет нам **400 Bad Request** и **403** в случае, если мы не прошли авторизацию. Метод **PATCH** мы используем для частичного обновления ресурса. В параметрах пути в URI мы передаем его идентификатор и в теле запроса те поля, которые хотим у него поменять. В случае успешного обновления ресурса сервер вернет нам **200 OK**. Если ресурс не найден, мы получим **404**. А метод **DELETE** мы используем для удаления ресурса. В случае успешного удаления сервер вернет нам **204 No Content**. Важно понимать, что в зависимости от бизнес-требований вариации ответов и различных статус-кодов на одном методе может быть сильно больше, чем я озвучил. Ещё пару слов про методы: методы могут быть **безопасными** и **идемпотентными**. Безопасный метод называют, если он не меняет состояние ресурсов на сервере, и идемпотентным метод называют, если при повторяющихся запросах он возвращает нам один и тот же результат. Давайте посмотрим на табличку, в которой показано, какой метод является безопасным и идемпотентным: - **GET** — и безопасный, и идемпотентный. - **POST** — небезопасный и неидемпотентный. - **PUT** — не является безопасным, но является идемпотентным. - **PATCH** — не является безопасным, но является идемпотентным. - **DELETE** — не является безопасным, но является идемпотентным. То есть сколько бы раз мы не долбанули запрос по методу **DELETE**, он всегда нам вернет **204 No Content**. А теперь вернемся к статус-кодам и закрепим информацию по ним: - **200 OK** — успешный ответ на запрос, возвращается, когда запрос успешно обработан и в ответе есть ресурсы, которые запрашивал клиент. - **201 Created** — означает, что создан новый ресурс, возвращается сам ресурс и его идентификатор при успешном создании. - **204 No Content** — запрос обработан, но в ответе нет содержимого, возвращается, например, при удалении ресурса. - **400 Bad Request** — сервер не может обработать данный запрос из-за ошибки в самом запросе, например, неправильных входных параметрах или теле запроса. - **401 Unauthorized** — требуется аутентификация для доступа к запрашиваемому ресурсу. - **403 Forbidden** — запрос не может быть выполнен, потому что пользователю отказано в доступе к какому-то ресурсу, то есть ответ отправляется, если у пользователя не хватает прав для получения этого ресурса. - **404 Not Found** — говорит нам о том, что запрашиваемый ресурс не найден. - **500 Internal Server Error** — говорит нам о том, что на сервере произошла ошибка, и он не смог успешно обработать запрос. Мы уже разобрались с основами, зафиксировали в нашей текстовой документации первые методы. Единственный ли это способ ведения документации в проекте? Нет, есть **Swagger** и **OpenAPI**. Swagger и OpenAPI — это инструменты для описания и документирования наших веб-сервисов и REST API. Как правило, Swagger представляет из себя отдельный endpoint на вашем проекте, в котором перечислены все методы, спецификации к ним, и вы прямо из него можете интерактивно в них потыкать и запрашивать данные. Описать Swagger можно вручную в формате YAML или JSON, или он может быть автогенерируемым. Давайте посмотрим на оба варианта. Перейдем на сайт [editor.swagger.io](https://editor.swagger.io) и задокументируем наши методы вручную. Пропишем все методы в YAML формате согласно документации. Я на этом заострять внимание не буду, вам нужно прогуглить и самостоятельно изучить, как описывать параметры в YAML формате в Swagger. Итак, справа мы видим список наших методов. Нажав на каждый из них, мы можем посмотреть список входных параметров, параметры ответа, а также попробовать отправить запрос, нажав на кнопочку **Try It Out** и **Execute**. А вот пример автоматической генерации Swagger с веб-фреймворком **FastAPI**. Мы создаем endpoint и в **Response Model** этого endpoint передаем ту модель данных, которую будем возвращать. Её мы описываем с помощью **Pydantic** моделей. Теперь по адресу `localhost/Dogs` мы увидим наш Swagger с нашим задокументированным методом. Прямо здесь мы можем его и протестировать. Теперь вернемся к проектированию наших методов. У нас еще оставались две таблички с двумя связями **One-to-Many**. Начнем с **One-to-Many**. Как же мы проектируем? Здесь у нас есть табличка **books** и табличка **pages**. У одной книги может быть много страниц. Спроектируем метод для страниц. Я буду всё проектировать в Swagger для удобства. Прежде всего, именно **One-to-Many**. URL порядок такой: версия API, название родительской сущности во множественном числе, дальше идентификатор родительской сущности, который относится к запрашиваемым дочерним сущностям, и название дочерней сущности во множественном числе. Для получения списка страниц мы используем endpoint `api/v1/books/{id}/pages`. То есть родитель во множественном числе, дальше параметр пути **ID** — идентификатор книги, к которой хотим получить страницы, и **pages** — название сущности во множественном числе, которую мы будем в итоге получать. Все методы мы проектируем аналогичным методом с книгами. Что касается связи **Many-to-Many**, у нас есть табличка **author_books**, которая говорит нам о том, что у одного автора есть несколько книг, и у одной книги есть несколько авторов. Я предпочитаю формировать URL для этого случая в виде названия таблички связи. То есть у нас будет `api/v1/author_books`. Какие методы мы можем здесь проектировать? Метод **POST** для привязки автора к книге или книги к автору, дальше методы для удаления этой привязки, а далее метод для получения списка книг конкретного автора и метод для получения списка авторов конкретной книги. Кроме того, мы можем расширить функциональность наших API методов путем ввода дополнительных параметров. Их мы можем передавать как в теле запроса, так и GET параметрами. Итак, какой первой модификацией мы можем совершить? Вернемся к endpoint, который отдает список книг. Нам важно не загрузить наш клиент данными. Если в нашей базе данных будет миллиард книг и сервер решит пачкой отдать их клиенту, клиент, скорее всего, ляжет. Чтобы это предотвратить, нам нужно **пагинация**. То есть со стороны клиента мы будем забирать данные частями и контролировать этот механизм на клиенте, передавая на сервер соответствующие параметры. Как можно сделать пагинацию? Есть два основных способа. Первый — через **limit** и **offset**. Мы вводим два GET параметра. Первый параметр **limit** определяет, сколько элементов мы хотим получить, то есть сколько книг мы выведем в браузере на нашей первой страничке, и параметр **offset** нужен для переключения между страничками. Для первой странички мы получили пять книг с лимитом пять, offset в данном случае **0**, то есть мы ничего не пропускаем. Дальше, чтобы перелистнуть страничку в браузере, мы нажимаем на цифру два. В данном случае нам нужно первых пять пропустить, то есть лимит у нас будет по-прежнему пять, и offset уже пять — это эквивалентно второй страничке в браузере. Бэкенд в данном случае нам обязан отдать количество элементов, которые есть у него всего, если мы хотим, например, отобразить количество возможных страниц для переключения. Второй способ — через **page** и **size**. Параметр **page** определяет номер страницы, которую мы хотим запросить, параметр **size** определяет количество элементов на странице. При первом запросе бэкенд обязан нам отдать ответ, в котором будет указано количество страниц, указанном **size**. То есть по факту это количество элементов в базе данных, разделить на **size**. Следующая модификация, которую мы можем провести, это **фильтрация**. То есть мы хотим запрашивать не все книги подряд, а, например, книги с конкретными названиями. Для этого в GET параметры или в тело запроса мы вводим дополнительный параметр **title**. Если фильтров много, то лучше переделать запрос на **POST** и передать их в теле запроса. Если их немного, то достаточно положить их в виде GET параметров. Например, у нас есть `libraryan.com/api/v1/books`, и мы хотим получить книги с названиями **Война и мир** и **Молчание небес**. Для этого вводим GET параметры **titles** с квадратными скобками, которые равны **Война и мир** и через амперсанд **title[]** равен **Молчание небес**. Квадратные скобки скажут серверу о том, что этот параметр необходимо распарсить в массив и отдать нам только те книги, названия которых есть в этом массиве. Если нам нужен **Range Filter**, так называемый, то есть диапазон значений, мы можем в квадратных скобках указать **LTE** и **GT**, то есть больше или равно чему-то или меньше или равно чему-то, предварительно написав название параметра. То есть, например, как здесь: **price[lte]=500** и **price[gt]=200**. Если мы хотим отсортированные... ...по убыванию мы перед названием поля пишем минус, например, `-title`, запятая, название второго поля, если оно у нас есть, по которому сортируем во вторую очередь. Примерно таким образом мы можем расширить функциональность наших API путем ввода новых параметров. Теперь скажу пару слов по поводу тестирования всего этого. Что касается бэкенда, на бэкенде REST API принято тестировать интеграционными тестами. То есть у нас должен быть некий клиент, который сможет сходить в наше приложение прямо из кода и протестировать входные параметры, тело ответа, различные статус-коды и прочее. Дальше мы можем тестировать вручную через Postman или писать автотесты с помощью других инструментов, проверяя максимальное количество тест-кейсов, покрывая всё, что только можно. Итак, сегодня мы разобрали большое количество теоретической базы по REST API, научились проектировать методы, расширять их функциональность, документировать и тестировать. Важно не останавливаться на этом, посмотреть еще ресурсы и найти что-то новое для себя. **Всем спасибо, у меня всё!**

Назад

Залогинтесь, что бы оставить свой комментарий

Copyright © StockChart.ru developers team, 2011 - 2023. Сервис предоставляет широкий набор инструментов для анализа отечественного и зарубежных биржевых рынков. Вы должны иметь биржевой аккаунт для работы с сайтом. По вопросам работы сайта пишите support@ru-ticker.com