Spring WebMVC Maven Dependency <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> Overview Spring MVC is blocking (because uses Java Servlet API) and non-reactive. MVC stands for Model View Controller and it is a common design pattern for GUI and Web Applications: Model is a POJO (Plain Old Java Object) class which means that it is not tied to any Java framework View handles rendering of the response which contains Model to HTML page Controller handles requests. It is responsible for invoking business logic and populating Model Spring Boot MVC Architecture Dispatcher Servlet receives request from Client passes the request to Controller and receives Model from Controller passes the Model to View and receives rendered HTML page passes rendered HTML page to Client The application.properties file should be placed under src/main/resources. Views should be placed under src/main/resources/templates. Controller should be a class and annotated with @Controller annotation @Controller public class BookController { // Logic calling Service } Service should be a class implementing an interface and annotated with @Service annotation @Service public class BookServiceImpl implements BookService { // Your Business Logic calling Repository } Repository should be an interface which does not have to be annotated with @Repository annotation public interface BookRepository extends JpaRepository<Book, Long> { // Query methods } Controller @Controller public class BookController { // ... @RequestMapping("/books") (1) public String getBooks(Model model) { model.addAttribute("books", bookService.findAll()); (2) return "books"; (3) } } 1 @RequestMapping maps web requests onto methods in request-handling classes with flexible method signatures. As a value you can provide the path mapping URIs 2 ⚪ Model#addAttribute(String attributeName, Object attributeValue) adds the supplied attribute under the supplied name in the model 3 Returns view name where we can iterate on books model attribute RestController @RestController A convenient annotation that is itself annotated with @Controller and @ResponseBody. @ResponseBody Indicates a method return value should be bound to the web response body. @RestController @RequestMapping("/api/v1/book") public class BookController { // ... @RequestMapping(method = RequestMethod.GET) public List<Book> getBooks() { return bookService.getBooks(); } } Specialized Request Mappings Instead of e.g. @RequestMapping(method = RequestMethod.GET) you can use convenient annotations: @GetMapping @GetMapping("/api/v1/book/{bookId}") public Book getBookById(@PathVariable UUID bookId) { return bookService.getBookById(bookId).orElseThrow(NotFoundException::new); // 200 } @PostMapping @PostMapping("/api/v1/book") public ResponseEntity postBook(@Validated @RequestBody BookDTO bookDTO) { BookDTO savedBookDTO = bookService.saveNewBook(bookDTO); URI location = URI.create("/api/v1/book/" + savedBookDTO.getId().toString()); return ResponseEntity.created(location).build(); // 201 } @PutMapping @PutMapping("/api/v1/book/{bookId}") public ResponseEntity updateBookById(@PathVariable UUID bookId, @Validated @RequestBody BookDTO bookDTO) { if (bookService.updateBookById(bookId, bookDTO).isEmpty()) { throw new NotFoundException(); } return ResponseEntity.noContent().build(); // 204 } @PatchMapping @PatchMapping("/api/v1/book/{bookId}") public ResponseEntity patchBookById(@PathVariable UUID bookId, @RequestBody BookDTO bookDTO) { if (bookService.patchBookById(bookId, bookDTO).isEmpty()) { throw new NotFoundException(); } return ResponseEntity.noContent().build(); // 204 } @DeleteMapping @DeleteMapping("/api/v1/book/{bookId}") public ResponseEntity deleteBookById(@PathVariable UUID bookId) { if (!bookService.deleteBookById(bookId)) { throw new NotFoundException(); } return ResponseEntity.noContent().build(); // 204 } URI Template Variable @PathVariable Indicates that a method parameter should be bound to a URI template variable. Supported for @RequestMapping annotated handler methods. @GetMapping("/api/v1/book/{bookId}") public Book getBookById(@PathVariable UUID bookId) { // ... } Exception Handling 🟢 DefaultHandlerExceptionResolver Resolves standard Spring MVC exceptions and translates them to corresponding HTTP status codes. It does not write content to the body of the response. Handle custom exception within specific Controller @ExceptionHandler(NotFoundException.class) (1) public ResponseEntity handleNotFoundException() { return ResponseEntity.notFound().build(); } 1 @ExceptionHandler handles exceptions in specific handler classes and/or handler methods. Here it handles custom 🟢 NotFoundException. Handle custom exception globally with @ControllerAdvice @ControllerAdvice (1) public class ExceptionController { @ExceptionHandler(NotFoundException.class) public ResponseEntity handleNotFoundException() { return ResponseEntity.notFound().build(); } } 1 @ControllerAdvice - specialization of @Component for classes that declare @ExceptionHandler, @InitBinder, or @ModelAttribute methods to be shared across multiple @Controller classes. Handle custom exception globally with @ResponseStatus @ResponseStatus(HttpStatus.NOT_FOUND, "Value Not Found") (1) public class NotFoundException extends RuntimeException { // Constructors... } 1 @ResponseStatus marks a method or exception class with the status code() and reason() that should be returned. It is alternative to @ControllerAdvice. 🟠 AbstractHandlerExceptionResolver Abstract base class for ⚪ HandlerExceptionResolver implementations. You can implement it to have full control over response (including body). 🟢 BasicErrorController Basic global error Controller included by Spring Boot, rendering ⚪ ErrorAttributes. This class can be extended for additional error response customization. 🟢 ResponseStatusException Exception added in Spring Framework 5 which allows setting the HTTP Status Code and Reason Exception Message in constructor. Spring Boot properties server.error.include-binding-errors=never - when to include "errors" attribute. server.error.include-exception=false - whether to include the "exception" attribute. server.error.include-message=never - when to include "message" attribute. server.error.include-stacktrace=never - when to include the "trace" attribute. server.error.path=/error - path of the error controller. server.error.whitelabel.enabled=true - whether to enable the default error page displayed in browsers in case of a server error.