Skip to content

Commit 7a72e6b

Browse files
committed
Sping Boot REST api validation + error handling
1 parent 3146f08 commit 7a72e6b

File tree

13 files changed

+58
-23
lines changed

13 files changed

+58
-23
lines changed

springboot-examples/springboot-restapi-validation/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<parent>
1212
<groupId>org.springframework.boot</groupId>
1313
<artifactId>spring-boot-starter-parent</artifactId>
14-
<version>2.1.4.RELEASE</version>
14+
<version>2.1.5.RELEASE</version>
1515
</parent>
1616

1717
<properties>

springboot-examples/springboot-restapi-validation/src/main/java/com/hellokoding/springboot/restful/ExceptionHandler.java renamed to springboot-examples/springboot-restapi-validation/src/main/java/com/hellokoding/springboot/restful/APIExceptionHandler.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
package com.hellokoding.springboot.restful;
22

3-
import lombok.RequiredArgsConstructor;
43
import lombok.extern.slf4j.Slf4j;
54
import org.springframework.http.HttpHeaders;
65
import org.springframework.http.HttpStatus;
76
import org.springframework.http.ResponseEntity;
7+
import org.springframework.validation.FieldError;
88
import org.springframework.web.bind.MethodArgumentNotValidException;
99
import org.springframework.web.bind.annotation.ControllerAdvice;
10+
import org.springframework.web.bind.annotation.ExceptionHandler;
1011
import org.springframework.web.context.request.WebRequest;
1112
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
1213

13-
@ControllerAdvice
14+
import javax.validation.ConstraintViolationException;
15+
1416
@Slf4j
15-
@RequiredArgsConstructor
16-
public class ExceptionHandler extends ResponseEntityExceptionHandler {
17+
@ControllerAdvice
18+
public class APIExceptionHandler extends ResponseEntityExceptionHandler {
1719
@Override
1820
protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
1921
log.error(ex.getMessage(), ex);
2022

23+
FieldError fieldError = ex.getBindingResult().getFieldError();
2124
ResponseDTO responseDTO = ResponseDTO.builder()
2225
.status(status.toString())
23-
.message(ex.getBindingResult().getFieldError().getDefaultMessage())
24-
.body(ex.getBindingResult().getTarget()).build();
26+
.message(fieldError.getDefaultMessage()).build();
27+
28+
return ResponseEntity.badRequest().body(responseDTO);
29+
}
30+
31+
@ExceptionHandler(ConstraintViolationException.class)
32+
public final ResponseEntity<Object> handleConstraintViolationException(Exception ex, WebRequest request) {
33+
log.error(ex.getMessage(), ex);
34+
35+
ResponseDTO responseDTO = ResponseDTO.builder()
36+
.status(HttpStatus.BAD_REQUEST.toString())
37+
.message(ex.getMessage()).build();
2538

2639
return ResponseEntity.badRequest().body(responseDTO);
2740
}

springboot-examples/springboot-restapi-validation/src/main/java/com/hellokoding/springboot/restful/Application.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
55

6+
67
@SpringBootApplication
78
public class Application {
89
public static void main(String[] args) {

springboot-examples/springboot-restapi-validation/src/main/java/com/hellokoding/springboot/restful/ResponseDTO.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
@Builder
88
public class ResponseDTO<T> {
99
private String status;
10-
private String message;
10+
11+
@Builder.Default
12+
private String message = "Success!";
13+
1114
private T body;
1215
}
Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package com.hellokoding.springboot.restful.product;
22

3+
import com.hellokoding.springboot.restful.ResponseDTO;
34
import org.springframework.beans.factory.annotation.Autowired;
45
import org.springframework.http.HttpStatus;
56
import org.springframework.http.ResponseEntity;
7+
import org.springframework.validation.annotation.Validated;
68
import org.springframework.web.bind.annotation.*;
79

810
import javax.validation.Valid;
9-
import java.util.List;
10-
1111

1212
@RestController
1313
@RequestMapping("/api/v1/products")
14+
@Validated
1415
public class ProductAPI {
1516
private final ProductService productService;
1617

@@ -20,29 +21,48 @@ public ProductAPI(ProductService productService) {
2021
}
2122

2223
@GetMapping
23-
public ResponseEntity<List<Product>> findAll() {
24-
return ResponseEntity.ok(productService.findAll());
24+
public ResponseEntity<ResponseDTO> findAll() {
25+
ResponseDTO responseDTO = ResponseDTO.builder()
26+
.status(HttpStatus.OK.toString())
27+
.body(productService.findAll()).build();
28+
29+
return ResponseEntity.ok(responseDTO);
2530
}
2631

2732
@GetMapping("/{id}")
28-
public ResponseEntity<Product> findById(@PathVariable @ProductIDExisting Long id) {
29-
return ResponseEntity.ok(productService.findById(id).get());
33+
public ResponseEntity<ResponseDTO> findById(@PathVariable @ProductIDExisting Long id) {
34+
ResponseDTO responseDTO = ResponseDTO.builder()
35+
.status(HttpStatus.OK.toString())
36+
.body(productService.findById(id)).build();
37+
38+
return ResponseEntity.ok(responseDTO);
3039
}
3140

3241
@PostMapping
33-
public ResponseEntity<Product> create(@Valid @RequestBody Product product) {
34-
return ResponseEntity.status(HttpStatus.CREATED).body(productService.save(product));
42+
public ResponseEntity<ResponseDTO> create(@RequestBody Product product) {
43+
ResponseDTO responseDTO = ResponseDTO.builder()
44+
.status(HttpStatus.CREATED.toString())
45+
.body(productService.save(product)).build();
46+
47+
return ResponseEntity.status(HttpStatus.CREATED).body(responseDTO);
3548
}
3649

3750
@PutMapping("/{id}")
38-
public ResponseEntity<Product> update(@PathVariable Long id, @Valid @RequestBody Product product) {
39-
return ResponseEntity.accepted().body(productService.save(product));
51+
public ResponseEntity<ResponseDTO> update(@PathVariable Long id, @RequestBody @Valid Product product) {
52+
ResponseDTO responseDTO = ResponseDTO.builder()
53+
.status(HttpStatus.ACCEPTED.toString())
54+
.body(productService.save(product)).build();
55+
56+
return ResponseEntity.accepted().body(responseDTO);
4057
}
4158

4259
@DeleteMapping("/{id}")
43-
public ResponseEntity delete(@PathVariable @ProductIDExisting Long id) {
60+
public ResponseEntity<ResponseDTO> delete(@PathVariable @ProductIDExisting Long id) {
4461
productService.deleteById(id);
4562

46-
return ResponseEntity.accepted().build();
63+
ResponseDTO responseDTO = ResponseDTO.builder()
64+
.status(HttpStatus.ACCEPTED.toString()).build();
65+
66+
return ResponseEntity.accepted().body(responseDTO);
4767
}
4868
}

springboot-examples/springboot-restapi-validation/src/main/java/com/hellokoding/springboot/restful/product/ProductIDExistingValidator.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.hellokoding.springboot.restful.product;
22

3-
import lombok.NoArgsConstructor;
43
import org.springframework.beans.factory.annotation.Autowired;
54
import org.springframework.stereotype.Component;
65

@@ -9,7 +8,6 @@
98
import java.util.Objects;
109

1110
@Component
12-
@NoArgsConstructor
1311
public class ProductIDExistingValidator implements ConstraintValidator<ProductIDExisting, Long> {
1412
@Autowired
1513
private ProductService productService;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
NotNull.name=This field is required
1+
NotNull.name=Name is required
22
ProductIDExisting=Product ID ${validatedValue} is not existing
File renamed without changes.

springboot-examples/springboot-restfulapi/src/main/java/com/hellokoding/springboot/restful/Application.java renamed to springboot-examples/springboot-restapi/src/main/java/com/hellokoding/springboot/restful/Application.java

File renamed without changes.

springboot-examples/springboot-restfulapi/src/main/java/com/hellokoding/springboot/restful/product/Product.java renamed to springboot-examples/springboot-restapi/src/main/java/com/hellokoding/springboot/restful/product/Product.java

File renamed without changes.

0 commit comments

Comments
 (0)