1. Введение
Со временем среда Spring стала стандартом де-факто для создания любых приложений на основе REST API. Spring предлагает множество готовых компонентов, чтобы избежать написания повторяющегося и громоздкого кода котельной плиты. Кроме того, прекрасная вещь о весне в том, что если есть готовое решение; он предлагает вам легкие способы интеграции с этой доступной библиотекой / структурой. В этой статье мы рассмотрим, как написать RESTful API на основе Spring с использованием технологии полного стека Spring; Spring Boot, Spring Validations и Spring data JPA с примером, который показывает полную информацию о следующем:
- Spring Boot и его конфигурация
- Управление зависимостями с помощью загрузчиков Spring
- Избегать узкого места в DAO-коде с использованием Spring-данных JPA.
- Поддержка Spring для валидации на уровне VO.
- Централизованная обработка исключений.
Мы используем Gradle для управления зависимостями и как инструмент для сборки. Давайте пройдемся по шагам.
2. Создайте проект
Вот шаги, которые необходимо было выполнить для создания проекта.
2.1 Spring Intializer
Spring предоставляет удобный инструмент для создания проектов в этом месте Spring INITIALIZR . На этой веб-странице вы можете загрузить свое приложение, добавив необходимые зависимости. Вы можете создать скелет проекта, добавив 3 зависимости, упомянутые ниже (см. Изображение ниже для ясного понимания).
1. «Веб»: эта зависимость необходима для кодирования веб-слоя и создания API. Когда проект генерируется, он отображается в виде следующей зависимости в файле build.gralde.
compile('org.springframework.boot:spring-boot-starter-web')
2. «Проверка»: чтобы включить проверку пружины. Это проявляется как следующая зависимость в build.gradle. compile('org.springframework.boot:spring-boot-starter-validation')
3. «JPA»: включить использование данных пружины JPA. Это проявляется как следующая зависимость в build.gradle.
compile('org.springframework.boot:spring-boot-starter-data-jpa')
2.2 Конфигурация Eclipse
Создайте проект и импортируйте его в затмение. Как только это будет сделано, все готово для создания вашего API. Проект, импортированный в затмение, должен выглядеть следующим образом.
3. Создайте API
Прежде чем писать API, давайте создадим пакеты в соответствии с соглашениями Java, как показано ниже.
Сгенерированный код, мы получаем класс в корневом пакете, то есть com.example.spring.springrestapp. Это наш класс загрузки.
Примечание. Классы запуска должны создаваться на уровне корневого пакета с настройками по умолчанию.
Теперь давайте продолжим и создадим класс для контроллера и метод API для добавления пользовательских данных в БД. Для этого API, который мы собираемся построить, давайте примем некоторые ограничения в качестве наших требований:
- Этот API должен собирать имя, фамилию, адрес электронной почты, адрес и номер телефона пользователя и сохранять их в БД MySQL.
- Вызывающие API передают имя, фамилию и адрес электронной почты в качестве обязательных полей. Электронная почта должна быть подтверждена для его формата.
- Домашний адрес и номер телефона могут быть необязательными.
4. Настройте детали БД:
Для этого проекта вам нужно иметь локальный экземпляр БД MySQL. Вы можете предоставить детали БД в application.properties, как показано ниже.
СВОЙСТВА ПРИМЕНЕНИЯ
|
1
2
3
|
spring.datasource.url = jdbc:mysql://localhost:3306/boot_appspring.datasource.username = rootspring.datasource.password = root |
В БД MySQL, давайте создадим таблицу, содержащую имя, фамилию, адрес электронной почты, адрес и номер телефона в БД MySQL, используя следующий скрипт.
СОЗДАТЬ СЦЕНАРИЙ ТАБЛИЦЫ
|
1
2
3
4
5
6
7
8
|
create table user_info(user_id smallint(10) primary key auto_increment,first_name varchar(150),last_name varchar(150),email varchar(200),address varchar(250),phone smallint(10)); |
5. Сконфигурируйте Spring Data JPA
Как только таблица будет готова, нам нужно отобразить ее на Java-объект, используя JPA. Каждое поле таблицы отображается в объекте java с использованием аннотаций. Ниже показано, как будет выглядеть наша сущность.
JPA ENTITY
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
package com.example.spring.springrestapp.dao.entity;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Table;import javax.validation.constraints.Email;import javax.validation.constraints.NotBlank;import org.springframework.data.annotation.Id;@Entity@Table(name = "user_info")public class UserInformation { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "USER_ID") private Integer userId; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "EMAIL") private String email; @Column(name = "ADDRESS") private String address; @Column(name = "PHONE") private Integer phone; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Integer getPhone() { return phone; } public void setPhone(Integer phone) { this.phone = phone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; }} |
Теперь создайте репозиторий данных JPA Spring. Репозитории JPA данных могут быть созданы путем расширения интерфейса JpaRepository, как показано ниже. (Обратите внимание, что вам нужно передать сущность и тип данных поля идентификатора. В моем примере сущность — это пользователь, а тип поля идентификатора — целое число)
JPA Хранилище
|
1
2
3
4
5
6
7
|
package com.example.spring.springrestapp.dao.repo;import org.springframework.data.jpa.repository.JpaRepository;import com.example.spring.springrestapp.dao.entity.UserInformation;public interface UserRepo extends JpaRepository {} |
Это просто, наш код DAO готов! Spring заботится о создании базовой реализации DAO.
6. Сервис и уровень контроллера
Давайте теперь создадим класс обслуживания только для того, чтобы сохранить данные пользователя. Вы можете добавить бизнес-логику в соответствии с вашими потребностями в методах.
СЕРВИС КЛАСС
|
1
2
3
4
5
6
7
8
9
|
@Servicepublic class UserDetailService { @Autowired private UserRepo userRepo; public UserInformation saveUser(UserInformation user) { return userRepo.save(user); }} |
Давайте теперь пойдем и создадим контроллер и метод API. API saveUser принимает данные json в теле запроса и возвращает ответ в виде JSON в теле.
СЛОЙ КОНТРОЛЛЕРА
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@RestController@RequestMapping("/api/user")public class SpringRestAppController { @Autowired private UserDetailService userService; @PostMapping(value = "/save") public @ResponseBody UserInformation createUser(@RequestBody UserInformation user) { return userService.saveUser(user); }} |
@RequestMapping используется для отображения ресурсов.
@PostMapping — это то же самое, что HttpPost, назначенный для @RequestMapping .
7. Настройка проверки уровня VO
Наш API нуждается в проверке данных, которые он получает в соответствии с требованиями, упомянутыми в начале. Для этого мы собираемся применить валидацию данных на уровне объекта, как показано ниже.
ПРОВЕРКА ДАННЫХ
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Entity@Table(name = "user_info")public class UserInformation { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "USER_ID") private Integer userId; @Column(name = "FIRST_NAME") @NotBlank(message = "first name can't be blank") private String firstName; @Column(name = "LAST_NAME") @NotBlank(message = "last name can't be blank") private String lastName; @Column(name = "EMAIL") @NotBlank(message = "email can't be blank") @Email(message = "invalid format") private String email; @Column(name = "ADDRESS") private String address; @Column(name = "PHONE") private Integer phone; |
Обратите внимание, что аннотация @NotBlank не позволяет пустым или нулевым значениям, а @Email проверяет допустимый формат электронной почты. Также мы добавили сообщения для неудачных проверок.
Теперь нам нужно сказать Spring, чтобы он делал валидации в соответствии с аннотациями, указанными в сущности. Для этого мы можем использовать аннотацию @Valid по запросу, как @Valid ниже.
|
1
2
3
4
|
@PostMapping(value = "/save")public @ResponseBody User createUser(@RequestBody @Valid UserInformation user) { return userService.saveUser(user);} |
8. Настройте обработку исключений
Если проверка не пройдена, мы должны предоставить правильно отформатированный ответ об ошибке потребителям API. Например, если имя не указано, я хочу вернуть сообщение об ошибке в следующем формате с неверным запросом кода ошибки HTTP. Не стоит давать трассировку стека исключений для пользователей API.
|
1
2
3
4
|
{"errorCode": "VALIDATION_FAILED","message": ""} |
Мы можем сделать это в каждом методе контроллера API или создать единую глобальную обработку исключений, чтобы избежать написания дублирующего кода в нескольких местах для одной и той же потребности.
Чтобы обработать центральное исключение в одном месте, мы используем обработчик ошибок Spring. В Spring 3.2 @ControllerAdvice предоставляется для глобализации обработки исключений / ошибок. Чтобы вернуть ответ об ошибке, давайте создадим VO с кодом ошибки и сообщением.
ОБРАЩЕНИЕ С ОШИБКАМИ В ЦЕННОМ ОБЪЕКТЕ
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@JsonInclude(content = Include.NON_NULL)@JsonIgnoreProperties(ignoreUnknown = true)public class ApiErrorVO { private String errorCode; private String message; public ApiErrorVO(String errorCode, String message) { super(); this.errorCode = errorCode; this.message = message; } |
Когда проверка не проходит, Spring создает MethodArgumentNotValidException . Мы можем перехватить это исключение и извлечь сообщение об ошибке из брошенного исключения. Мы используем @ExceptionHandler чтобы перехватить исключение, как @ExceptionHandler ниже.
ОБРАБОТКА ИСКЛЮЧЕНИЙ
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@ControllerAdvicepublic class ApiExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) @ResponseBody public ApiErrorVO handleValidationError(MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); FieldError fieldError = bindingResult.getFieldError(); String defaultMessage = fieldError.getDefaultMessage(); return new ApiErrorVO("VALIDATION_FAILED", defaultMessage); }} |
@ResponseStatus используется для указания статуса HTTP Bad request.
@ResponseBody гарантирует, что ошибка будет записана в тело ответа.
9. Вывод
Теперь давайте проверим API.
Случай 1: ошибка проверки
URL: http://localhost:8080/restApp/api/user/save
RequestPayload: обратите внимание на пустое имя
|
1
2
3
4
5
|
{"firstName":"","lastName":"K","email":"alexk@abc.com"} |
Ответ: с Http Status 400
|
1
2
3
4
|
{"errorCode": "VALIDATION_FAILED","message": "first name can't be blank"} |
Случай 2: когда предоставлены все необходимые значения
Запросить полезную нагрузку:
|
1
2
3
4
5
|
{"firstName":"Alex","lastName":"K","email":"alexk@abc.com"} |
Ответ: С Http Status 200
|
1
2
3
4
5
6
7
8
|
{"userId": 8,"firstName": "Alex","lastName": "K","email": "alexk@abc.com","address": null,"phoneNumber": null} |
На этом тест заканчивается.



