Representational state transfer (REST) is an architectural style that describes best practices to expose web services over HTTP, emphasizing performance, scalability, simplicity, modifiability, visibility, portability, and reliability.
REST exposes resources through URIs using nouns (not verbs), supporting a limited set of operations (GET, PUT, POST, DELETE). Clients can request particular representations (HTML, XML, JSON, etc) that can link to other resources, and use Hypermedia as the Engine of Application State (HATEOAS). It uses a stateless architecture, headers, and status codes to communicate with clients.
Spring MVC provides REST support. The RequestMapping annotation is used to map web requests. The RequestMethod enumerators are: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, and TRACE.
@RequestMapping(path="/accounts", method=RequestMethod.GET)
Since Spring 4.3, instead of RequestMapping you can use these annotations:
- @GetMapping: To fetch a resource
- @PostMapping: To create a new resource
- @PutMapping: To update entirely a resource
- @DeleteMapping: To delete an existing resource
- @PatchMapping: To update partially a resource
For HEAD, OPTIONS, and TRACE use RequestMethod enumerators.
@GetMapping(value = "/accounts")
public @ResponseBody List<Account> accountSummary() {
return accountManager.getAllAccounts();
}
@GetMapping(value = "/accounts/{id}")
public @ResponseBody Account accountDetails(@PathVariable int id) {
return retrieveAccount(id);
}
HTTP GET returns data in the response body. The @ResponseBody annotation allows us to return a serialized object. If the class is annotated as a @RestController, there is no need for @ResponseBody on GET methods.
To have more control over the response (e.g, set headers and control content), you can use ResponseEntity:
@GetMapping(value = "/accounts/{id}")
public ResponseEntity<Account> accountDetails(@PathVariable long id) {
Account account = accountService.getAccount(id);
return ResponseEntity
.ok()
.header("Last-Modified", account.getLastUpdate())
.body(account);
}
In the example above, the ResponseEntity is built calling the ok method, which returns the 200 status code (success). See HttpStatus documentation. You also can add the @ResponseStatus annotation over the method.
Similarly to @ResponseBody, the @RequestBody annotation allows us to convert automatically incoming data based on the content type of the request.
@PostMapping(value = "/accounts")
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Void> createAccount(@RequestBody Account newAccount) {
Account account = accountManager.save(newAccount);
URI location = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/{childId}")
.buildAndExpand(account.getEntityId()).toUri();
return ResponseEntity.created(location).build();
}
In the previous example, we are building the URI. To create a URI you can use ServletUriComponentsBuilder or UriComponentsBuilder (for hard-coded URLs).
Spring MVC provides a Spring REST Client called RestTemplate (Spring Framework 5 introduced a new HTTP client called WebClient. See Reactive Spring Applications). RestTemplate provides access to RESTful services and supports all the HTTP methods. See RestTemplate documentation
- DELETE: delete method
- GET: getForObject and getForEntity (for ResponseEntity) methods
- HEAD: headForHeaders method
- OPTIONS: optionsForAllow method
- POST: postForLocation, postForObject and exchange (for RequestEntity) methods
- PUT: put method
@Test
public void listAccounts() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/accounts";
Account[] accounts = restTemplate.getForObject(url, Account[].class);
assertNotNull(accounts);
}
Finally, Spring HATEOAS provides an API for generating links in MVC Controller responses. Use Link class and @EnableHypermediaSupport annotation.
@Controller
@EnableHypermediaSupport(type = { HypermediaType.HAL })
public class EmployeeController {
@GetMapping(value = "/{employeeId}")
public Employee getEmployeeById(@PathVariable long employeeId) {
Employee employeeById = ControllerLinkBuilder.methodOn(EmployeeController.class).getEmployeeById(employeeId);
Link selfLink = ControllerLinkBuilder.linkTo(employeeById).withSelfRel();
Employee employee = employeeService.getEmployee(employeeId);
employee.add(selfLink);
return employee;
}
}
Top comments (0)