Создание REST API CRUD приложения

Создадим crud приложение только теперь с использованием REST.

Создадим новое Maven приложение с такими папками и файлами.

Содержимое файлов в этом crud приложении ничем не отличается от содержимого предыдущего crud приложения, которое мы создавали, за исключением содержимого файла контроллера и pom файла, в который просто нужно добавить зависимость для поддержки jackson.

Рассмотрим содержимое файла контроллера, в котором собственно пишется api.

Заметьте что в Mapping-ах внизу повторяющиеся адреса /actors.

Это стандартная практика в spring rest приложениях, что для разных операций с тем или иным конкретным классом (в данном случае с Actor) используется один и тот же адрес, а получатель запроса (получатель это RestController) будет распознавать какой из обработчиков вызывать основываясь на HTTP методе присланного запроса

То есть крайне не желательно называть адреса по разному. Например: /getallactors, /getactorfromdb, /savesingleactor/{actorID} и т.д.

Лучше пусть контроллер сам распознает какой из обработчиков вызывать по HTTP методу запроса.

  • Get запрос будет вызывать обработчик помеченный аннотацией @GetMapping,
  • Post запрос будет вызывать обработчик помеченный аннотацией @PostMapping,
  • Put запрос будет вызывать обработчик помеченный аннотацией @PutMapping,
  • Delete запрос будет вызывать обработчик помеченный аннотацией @DeleteMapping)
package com.MavenWebApps.RestWebApplicationCRUD.controller; import java.util.List; @RestController public class MainController { @Autowired private ServiceInterface serviceclass; //READ. Всех актеров @GetMapping(“/actors”) public List getActors() { return serviceclass.getListOfActors(); } //READ. Одного актера @GetMapping(“/actors/{actorID}”) public Actor getActor( @PathVariable int actorID){ Actor actor = serviceclass.getActor(actorID); return actor; } //CREATE @PostMapping(“/actors”) public Actor addActor( @RequestBody Actor actor) { //Из нового здесь можно увидеть //аннотацию @RequestBody, //но здесь ничего сложного. //Когда клиентом посылается JSON //методом POST в наш контроллер //средства Jackson конвертируют //за кулисами этот JSON в объект actor //и мы можем использовать этот объект. //В этом обработчике мы добавляем //присланного актера в базу. serviceclass.saveActor(actor); return actor; } //UPDATE @PutMapping(“/actors”) public Actor updateActor( @RequestBody Actor actor) { serviceclass.saveActor(actor); return actor; } //DELETE @DeleteMapping(“/actors/{actorID}”) public String deleteActor( @PathVariable int actorID) { serviceclass.deleteActor(actorID); return “Deleted customer id – ” +actorID; } }

Давайте же протестируем созданные обработчики.

В таблице actor сейчас три актера:

Давайте получим данные всех актеров в формате JSON через обработчик getActors. Этот обработчик извлекает актеров из БД помещает их в List и отправляет этот List с актерами в формате JSON.

Обратимся к этому обработчику через Rest Client по ссылке /actors запросом GET. RestController распознает, что был сделан GET запрос и вызывает обработчик помеченный аннотацией GetMapping.

Как видим, List с объектами актеров из БД успешно пришел в формате JSON в ответ на GET запрос.

Давайте получим данные одного конкретного актера в формате JSON через обработчик getActor. Этот обработчик извлекает конкретного актера из БД по присланному в ссылке id этого актера и отправляет его в формате JSON.

Обратимся к этому обработчику через Rest Client по ссылке /actors/2 запросом GET чтобы получить JSON актера с id два. RestController распознает, что был сделан GET запрос и вызывает обработчик помеченный аннотацией GetMapping. Но в этот раз уже тот где ссылка /actors/{actorID}, а не просто /actors

Как видим обработчик успешно извлек актера с id два и отправил его в ответ на GET запрос в формате JSON.

Теперь давайте добавим нового актера в БД через обработчик addActor. Этот обработчик получает информацию нового актера в формате JSON, конвертирует этот JSON в объект класса Actor и добавляет этот объект в БД. Также он возвращает данные добавленного актера в формате JSON в ответ на POST запрос.

Обратимся к этому обработчику через Rest Client по ссылке /actors запросом POST, при этом во вкладке BODY пишем в формате JSON данные актера, которого мы хотим отправить в контроллер и добавить БД. RestController распознает, что был сделан POST запрос и вызывает обработчик помеченный аннотацией PostMapping.

Выше видим, что обработчик успешно отправил в ответ на POST запрос данные добавленного в БД актера в формате JSON. Ниже можно увидеть, что актер успешно добавился в БД.

Теперь давайте обновим присутствующего актера в БД через обработчик updateActor. Этот обработчик получает новую информацию для обновления существующего в БД актера в формате JSON, конвертирует этот JSON в объект класса Actor и обновляет актера в БД исходя из id, который был передан в JSON. Также он возвращает данные обновленного актера в формате JSON в ответ на PUT запрос.

Обратимся к этому обработчику через Rest Client по ссылке /actors запросом PUT, при этом во вкладке BODY пишем в формате JSON данные актера, которого мы хотим отправить в контроллер и обновить в БД, при этом в отправляемом JSON обязательно нужно указать id актера, которого мы хотим обновить. RestController распознает, что был сделан PUT запрос и вызывает обработчик помеченный аннотацией PutMapping.

Выше видим, что Обработчик успешно отправил в ответ на PUT запрос данные обновленного в БД актера в формате JSON. Ниже можно увидеть, что актер с id четыре успешно обновился в БД. Naomi Watts изменилось на Michael Pitt.

Давайте удалим конкретного актера из БД через обработчик deleteActor. Этот обработчик удаляет конкретного актера из БД по присланному в ссылке id этого актера и отправляет сообщение, что актер с таким-то id удален из БД.

Обратимся к этому обработчику через Rest Client по ссылке /actors/4 запросом DELETE чтобы удалить из БД актера с id четыре. RestController распознает, что был сделан DELETE запрос и вызывает обработчик помеченный аннотацией DeleteMapping.

Выше видим, что Обработчик успешно отправил в ответ на DELETE запрос сообщение о том, что удален актер с id четыре. Ниже можно увидеть, что актер с id четыре успешно удалился из БД.

Первое Spring REST API-приложение

Создадим свое собственное Rest Api. То есть мы создадим набор методов, к которым можно будет обращаться http запросом по ссылке. Эти методы будут возвращать java объекты конвертированные в JSON тому кто сделал запрос.

Создадим Мавен приложение с такой структурой и файлами:

В файле MainController будут находиться обработчики, которые будут принимать http запрос, конвертировать java объекты в JSON и отправлять этот JSON обратно тому кто сделал запрос.

В classes находятся классы, объекты которых будут конвертироваться в JSON.

Теперь давайте добавим необходимые зависимости в pom файл чтобы приложение работало. А именно Jackson для конвертации из JSON в объект и наоборот.

4.0.0 com.MavenWebApp RestWEBapp jar 0.0.1-SNAPSHOT hibernateCRUDapp Maven Webapp http://maven.apache.org org.springframework spring-webmvc 5.3.0 org.springframework spring-tx 5.3.0 org.springframework spring-orm 5.3.0 org.hibernate hibernate-core 5.3.26.Final mysql mysql-connector-java 8.0.32 com.mchange c3p0 0.9.5.5 javax.servlet javax.servlet-api 3.1.0 javax.servlet jstl 1.2 junit junit 3.8.1 test com.fasterxml.jackson.core jackson-databind 2.15.2 org.apache.maven.plugins maven-war-plugin 3.4.0 false

Обработчики в нашем приложении будут конвертировать объект в JSON подобный тому, что приведен ниже.

{ “musician_name”: “Behoven”, “isclassic”: true, “numofoperas”: 150, “gratest_works”: [“Archduke Trio”, “Missa Solemnis”, “Symphony No. 5”], “theMostFamousWork”: { “work_name”: “Symphony No. 3 ‘Eroica”, “dedicatedTo”: “to Napoleon”, “ComposedIn”: 1802 }, “somePropToIgnore”: “ignoreMe” }

Этот JSON хранит данные о музыканте Bethoven.

Здесь можно увидеть все типы информации, которые можно хранить в JSON.

В первых трех записях можно увидеть, что можно хранить разные типы данных, в нашем случае – string, boolean и int.

В четвертой записи можно увидеть, что в JSON также можно хранить массивы. Его элементы перечисляются в квадратных скобочках.

В пятой записи можно увидеть, что в JSON также можно хранить вложенный JSON. В нем хранятся данные, которые описывают самую известную работу автора Bethoven.

Зачем шестая запись дальше будет объеснено.

Давайте создадим класс музыканта объект которого будет конвертироваться в JSON подобный тому, что приведен выше и наоборот должна быть возможность конвертации JSON выше в объект класса ниже. Как можно увидеть он содержит такие же типы информации как и json, который был выше – три поля, массив и вложенный объект. Как уже говорилось для успешной конвертации имена сеттеров и геттеров в этом классе должны содержать имена записей из JSON.

package com.MavenWebAps.RestWebApp.convertFromJsonjson; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; //Аннотация @JsonIgnoreProperties нужна просто //чтобы приложение не выдало ошибку если в классе //нет каких-то полей, которые есть в JSON, который //конвертируется в объект на основе этого класса. //В нашем случае это “somePropToIgnore”: “ignoreMe”. //Как можно увидеть в классе ниже нет поля //соответствующего записи //”somePropToIgnore”: “ignoreMe” в JSON. @JsonIgnoreProperties(ignoreUnknown=true) public class Musician { private String musician_name; private boolean isclassic; private int numofoperas; private String[] gratest_works; //В JSON файле приведенном ранее можно //увидеть вложенный JSON: “theMostFamousWork”: //{“work_name”: “Symphony No. 3 ‘Eroica'”, …} //Этот вложенный JSON сконвертируется как //внутренний объект объекта класса Musician. //Класс этого внутреннего объекта нам тоже нужно //создать. Поэтому ниже в следующем примере //кода после текущего создается //класс TheMostFamousWork. private TheMostFamousWork theMostFamousWork; public Musician() { } public Musician(String musician_name, boolean isclassic, int numofoperas) { this.musician_name = musician_name; this.isclassic = isclassic; this.numofoperas = numofoperas; } //Заметьте что названия геттерам и сеттерам даны //в соответствии с именами данных в JSON, то есть //левой части записей. Например сеттер и геттер //ниже для записи “musician_name” : “Behoven” //содержит имя левой части этой записи. //Имена очень важно давать именно такие //чтобы конвертация происходила успешно. public String getMusician_name() { return musician_name; } public void setMusician_name( String musician_name) { this.musician_name = musician_name; } public boolean isIsclassic() { return isclassic; } public void setIsclassic(boolean isclassic) { this.isclassic = isclassic; } public int getNumofoperas() { return numofoperas; } public void setNumofoperas(int numofoperas) { this.numofoperas = numofoperas; } public String[] getGratest_works() { return gratest_works; } public void setGratest_works( String[] gratest_works) { this.gratest_works = gratest_works; } public TheMostFamousWork getTheMostFamousWork() { return theMostFamousWork; } public void setTheMostFamousWork( TheMostFamousWork theMostFamousWork) { this.theMostFamousWork = theMostFamousWork; } }

Создадим теперь класс вложенного в музыканта объекта.

*TheMostFamousWork.java package com.MavenWebApps.RestWEBApp.convertfromjsoncl; public class TheMostFamousWork { private String work_name; private String dedicatedTo; private int ComposedIn; public TheMostFamousWork() { } public String getWork_name() { return work_name; } public void setWork_name(String work_name) { this.work_name = work_name; } public String getDedicatedTo() { return dedicatedTo; } public void setDedicatedTo(String dedicatedTo) { this.dedicatedTo = dedicatedTo; } public int getComposedIn() { return ComposedIn; } public void setComposedIn(int composedIn) { ComposedIn = composedIn; } }

Давайте же создадим Api, то есть набор методов, которые можно вызывать по ссылке используя http запросы. Делается это конечно же в контроллере, который представлен ниже.

package com.MavenWebAps.RestWEBApp.controller; import java.util.ArrayList; //Напишем свое REST Api! //В Spring MVC для обработки http методов и автоматической //конвертации из JSON в java объекты используеться //@RestController вместо @Controller, который был раньше. @RestController public class MainController { //Этим обработчиком просто отправляем в ответ //тому кто сделал запрос по адресу /getMusician //объект класса Musician в JSON формате. //Конвертация Java объектов в JSON и наоборот //происходит “за кулисами” средствами Jackson, //ничего специального для конвертации делать //не нужно. Просто возвращаем объект из метода //и он автоматически отправляеться тому кто //вызвал метод по ссылке /getMusician //в JSON формате. @RequestMapping(“/getMusician”) public Musician getMusician() { return new Musician(“Bethoven”,true,150); } //Помним что результат запроса SELECT для извлечения //нескольких записей из таблицы, который совершаеться //средствами hibernate, записываеться в List. //Соответственно удобно будет сразу отправлять клиенту //этот List с извлеченными данными таблицы как ответ. //С помощью Jackson можно отправить List в JSON формате. //Обработчик ниже возвращает клиенту List JAVA объектов //в формате JSON. Конвертация Java объектов в JSON //происходит “за кулисами” средствами Jackson. //Нашему API нужно просто вернуть List с обьектами //и клиент получит объекты списка в формате JSON. @RequestMapping(“/getMusicians”) public List getMusicians() { List musicians = new ArrayList(); musicians.add(new Musician(“Bethoven”,true,150)); musicians.add(new Musician(“Mozart”,true,250)); musicians.add(new Musician(“Bach”,true,120)); //Возвращаем List return musicians; //То есть можно отправить не один объект //как это делает прошлый обработчик, а сразу много. } //Чтобы клиент мог получить конкретного музыканта из списка //он может передать число в путь как показано ниже. //Это число это индекс в списке и по этому индексу извлекается //конкретный музыкант в списке. @RequestMapping(“/getSingleMusicianFromList/{musicianID}”) //Аннотация @PathVariable позволяет получить этот //индекс из пути запроса. Имя аргумента метода должно //совпадать с именем переменной в URL. public Musician getSingleMusicianFromList( @PathVariable int musicianID) { List musicians = new ArrayList(); musicians.add(new Musician(“Bethoven”,true,150)); musicians.add(new Musician(“Mozart”,true,250)); musicians.add(new Musician(“Bach”,true,120)); //С помощью get достаем из списка нужного музыканта //исходя из переданного клиентом числа и отправляем //его клиенту в JSON формате. return musicians.get(musicianID); } }

Давайте проверим первый обработчик. Вернет ли он нам объект музыканта Bethoven в формате JSON.

Вообще тест можно было бы совершить через адресную строку браузера, как раньше, поскольку в нашем приложении пока только Get запросы, но будем привыкать к Rest Client, так как скоро появятся и post запросы с помощью которых клиентом будет отправляться JSON в обработчик и другие виды запросов.

Перейдем по ссылке getMusician чтобы сделать запрос к первому обработчику:

Как видим обработчик успешно создал объект, конвертировал его в JSON и отправил этот JSON как ответ на запрос.

Кстати, как можно было заметить, вложенный объект мы не создавали, он null. Это было сделано просто чтобы не было слишком много JSON и чтобы показать, что можно конвертировать и отправлять не полностью заполненный объект.

Далее протестируем второй обработчик. Как мы помним, он должен возвращать много объектов конвертированных в JSON.

Как видим, обработчик успешно конвертировал объекты в списке в JSON и отправил эти JSON как ответ на запрос.

Теперь давайте с помощью третьего обработчика извлечем из списка объектов второй объект. Как мы помним счет начинается с нуля, поэтому чтобы обратиться ко второму элементу в списке пишем в конце ссылки 1.

Как мы помним, второй музыкант в списке был моцарт и как мы видим обработчик успешно его выбрал из списка и прислал в формате JSON.

Что такое REST API?

REST API – простой способ коммуникации между приложениями основанный на http методах передачи данных. Помним эти методы – GET, POST и др.

Приложения могут обмениваться данными в различных форматах. То есть обмен данными между приложениями может происходить например в формате обмена XML файлами. Также обмен может происходить с помощью JSON, HTML или др.

Приложения между которыми происходит обмен могут быть написаны на разных языках, главное чтобы эти приложения были способны пропарсить (это значит считать данные из файла) передаваемые файлы с данными (XML, JSON или HTML файлы).

Что же такое API?

API — это механизмы (или проще говоря, способы), с помощью которых клиентское приложение может обратиться к серверному приложению. Точнее, это конкретные методы (функции) на сервере, к которым можно обратиться по URL через HTTP.

Кроме самих методов, API включает правила, как правильно к ним обращаться: какой HTTP-метод использовать (GET, POST и т.д.), какие данные передавать и что можно получить в ответ.

Если говорить ещё точнее, API — это интерфейс серверного приложения, то есть набор доступных снаружи способов взаимодействия с сервером без знания его внутреннего устройства.

То есть под API-интерфейсом понимается то, что мы описали выше: серверные HTTP-обработчики и правила, как к ним обращатьсябез знания внутренней реализации обработчиков.

У каждого специализированного веб-приложения может быть свое собственное особенное API.

Example

Например, API приложения для покупки билетов.

У сервиса для покупки билетов есть API, который позволяет клиентскому приложению отправить запрос на бронирование или оплату. Сервер обрабатывает этот запрос и возвращает результат.

У многих известных веб-сервисов, как правило, есть свое API. У Facebook, GitHub и т.д. API, как уже говорилось, это методы(функции) для обмена данными определенные в программах этих веб-сервисов. Вышеупомянутые веб-ресурсы предоставляют ссылки для обращения к своим методам. То есть кто угодно может сделать запрос к какому-либо методу веб-сервиса по этим ссылкам и получить от этого веб-сервиса какие-то данные.

Давайте для примера посмотрим на страницу с перечислением Api методов сервиса FaceBook предназначенных для сбора статистики FaceBook страницы:

Как видим, нам предлагается ссылка по которой мы можем вызвать конкретный метод сервиса FaceBook, который вернет нам JSON с данными о том сколько человек взаимодействовало с конкретной указанной в ссылке FaceBook страницей.

Таким образом через ссылки можно взаимодействовать с Api Фейсбука.

Есть не один архитектурный стиль построения APIRPC, SOAP, REST и другие. Мы будем рассматривать REST стиль, так как он самый популярный.

Отличительной особенностью REST API от других стилей является то, что этот стиль лучше подходит для взаимодействия клиентского приложения с ресурсом (например с БД), который находиться на серверном приложении. То есть это как раз хорошо подходит для того чтобы клиент совершал CRUD операции с ресурсом на сервере.

Как уже говорилось, обменивающиеся данными приложения могут быть написаны на разных языках, ГЛАВНОЕ чтобы они оба использовали REST стиль и были в состоянии прочитать отправляемые друг другу данные.


JSON, Jackson

Самый популярный формат обмена данными сегодня это конечно же JSON.

JSON – просто строка с данными.

Простой пример:

{ “musician_name”:”Behoven”, “isclassic”: true, “numofoperas”: 150 }

Как видим данные хранятся в JSON в формате пар (то есть например “musician_name”:”Behoven” это одна пара) и их может прочитать и обработать любой язык программирования.

JSON строку можно превратить в Java объект и наоборот (это называется Data binding).

При конвертации JSON в Java объект пара превращается в поле в этом объекте. Левое значение в паре это имя поля объекта – правое это значение поля с таким именем.

То есть в обеъкте, который получиться по итогу конвертации JSON файла выше будет содержаться строковое поле с именем musician_name и значением “Behoven”, будет содержать boolean поле с именем isclassic и значением true и будет содержать числовое поле с именем numofoperas и значением 150.

Jacksonсамое популярное средства для таких конвертаций.

При конвертации ИЗ JSON в объект вызываются нужные setter-ы объекта в который происходит конвертация, при конвертации А когда В JSON из объекта нужные getter-ы.

При создании класса объекта, который будет конвертироваться в JSON и наоборот, для успешной конвертации нужно называть геттеры/сеттеры в нем в соответствии с теми элементами пар JSON, что слева (например musician_name – setMusician_name(), isclassic – setIsclassic()…) иначе jackson не найдет getter/setter методы чтобы совершить конвертацию.


Содержимое запроса/ответа, коды статуса

HTTP запрос/ответэто набор данных.

Example

HTTP запрос состоит из 3 частей:

  • В Request line храниться название метода (POST,GET,…),
  • Header Variablesдоп. инф., например формат отправляемых данных (JSON,xml,…), размер данных и т.д.,
  • Message Body – собственно отправляемый JSON или xml или др.

HTTP ответ отличается от запроса только первым элементом:

В HTTP ответе в Response line храниться код статуса ответа сервера (типа 404 – сервер сообщает этим кодом, что запрашиваемые клиентом данные не найдены на сервере (виноват клиент, что запросил несуществующие данные, поэтому это клиентская ошибка), или 200 – сервер сообщает в ответе этим кодом, что всё прошло успешно).

Вообще коды статуса ответа бывают такие:

  • 100-199 инф-ые,
  • 200-299удача,
  • 300-399перенаправление,
  • 400-499ошибки на клиенте,
  • 500-599ошибки на сервере.

REST клиент

Чтобы сделать небольшой тест отправки HTTP запроса и получения HTTP ответа необходимо установить REST клиент.

Скачаем программу Advanced REST Client по ссылке: https://www.advancedrestclient.com/.

REST клиент это такое средство для тестирования REST приложения.

Допустим мы создали серверное REST приложение, которое принимает JSON от клиентского приложения и отправляет ему в ответ на этот JSON другой JSON.

И нам нужно протестировать этот механизм. То есть нам нужно какое-то средство для того чтобы отправить JSON в наше серверное REST приложение и прочитать ответ, который серверное REST приложение отсылает. Это средство и есть REST клиент.

Их есть много разных в интернете, но мы воспользуемся Advance REST Client.


Тестирование с помощью Rest Client

Отправим GET запрос по ссылке https://api.myip.com (это такой REST API для получения своего IP адреса в формате JSON).

В ARC выберем GET запрос, вобьем ссылку куда будем делать запрос и нажмем send the request.

На картинке ниже можно увидеть все три элемента HTTP ответа – статус 200, headers и сам response c нашим ip.

Это был пример обмена данными в формате JSON между двумя приложениями.

Одно клиентское – Advanced REST Client, другое серверное – https://api.myip.com