Spring Boot rest (with HATEOAS)
Example of Unittest for SpringBoot Rest Controller
rest general principles
Representational State Transfer (REST) is a collection of (six) architectural constraints:
- Client-server architecture
- Statelessness
- Cacheability
- Layered system
- Code on demand
- Uniform interface
- Resource identification in requests (e.g. URI)
- Resource manipulation through representations (client can use crud on a resource via one of its representations)
- Self-descriptive messages (e.g. MediaType)
- Hypermedia as the Engine of Application State (HATEOAS)
Hypermedia as the Engine of Application State (HATEOAS): you can find all available resources through the publication of links which point to these resources. Like a landing page of a website which leads to all further links on the website.
spring rest (via spring mvc)
Official hands on guide with emphasis on hateoas Nice overview of topic related to rest
- needs spring-boot-starter-web as dependecy (not spring-boot-starter-jersey which is Jax_RS (Java EE))
- annotate class with @RestController (shorthand for @ResponseBody and @Controller) and @RequestMapping("/")
- use following annotations to mark a method to be exposed via
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
- RequestMapping (generic mapper/ old way)
- the @*Mapping annotations allows:
- to define which http verb is used
value=""
. to define under which path the method is called (based on the path defined in @RequestMapping on the class)produces=
: to define which representation formats are allowed- to to define path parameters e.g. @GetMapping("/{id}/specialpath")
- @ResponseStatus on a method defines the http code for a successful call
- On method parameters:
- @RequestParam defines it to be a query-string (?id=...&name=...)
- @PathVariable defines it as a path variable (http://rooturl.de/books/myPathVariable/)
- @RequestBody binds the parameter to the body of the HTTP (usually used by http post)
- @ModelAttribute for complex objects as query params or post bodys
- note: @PathParam and @QueryParam, are the jax-rs annotations
spring hateoas (HAL by default)
- add spring-boot-starter-hateoas dependency to add HATEOAS support.
- You have multiple options to create a RepresentationModel
which can hold links: - create a class which extends RepresentationModel
. (to set the name of the entity in the representation use the org.springframework.hateoas.server.core.Relation Annotation) - wrap a collection in a CollectionModel
- use EntityModel to wraps an existing class (
EntityModel.of(person)
)
- create a class which extends RepresentationModel
- add a selfLink and optional further links to the RepresentationModels you want to return
- use Methods from WebMvcLinkBuilder like linkTo() to return
_links
to relevant operations - implement RepresentationModelAssembler and the toModel method for every entity (in order to remove the code from the RestController)
LEGACY: Spring HATEOAS Version 1.0 dropped the resourceSupport/Resource/Resources/PagedResources group of classes
error handling
- @ControllerAdvice works on global level for all controller beans and allows to save all @ExceptionHandler in one place
- ResponseStatusException allows to define error handling for one method
- @ExceptionHandler works only for the class where a method is annotated with.
- HandlerExceptionResolver can be extended or just use predefined implementations
- Spring also support "Problem Details for HTTP APIs" (RFC 7807 and RFC 9457)
spring.mvc.problemdetails.enabled
- Example here
parsing date query parameters
on parameter level
@DateTimeFormat(pattern="MM/dd/yyyy")
before the parameter- or
@DateTimeFormat( iso=DateTimeFormat.ISO.<>)
before the parameter
on application level
@Configuration(proxyBeanMethods = false)
class RestConfiguration {
public DateTimeFormatterRegistrar registrar() {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar()
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
registrar.setTimeFormatter(DateTimeFormatter.ofPattern("HH:mm:ss"));
registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
return registrar;
}
}
- Set default value for a LocalDate param
methodname(@RequestParam(defaultValue="#{T(java.time.LocalDate).now()}") LocalDate date)
- calls to
<resturl>?requestParam=
or<resturl>?requestParam=&requestParam2=bla
or just<resturl>
would trigger the default value
cache control
evolving api
Add new fields to your JSON representations, but do not take any away (Never delete a column in a database)
Test RestController
Use TestRestTemplate and just call the endpoints. Only make rudimentary assertions or such which can only be done on Rest like error page. Test the real logic in the service classs which provides the information to the restcontroller.
spring data rest
When adding spring-boot-starter-data-rest as dependency to your spring boot project all spring data repositories will be exposed via rest api.
- by default api root is under /
- root can be changed via spring.data.rest.basePath property
- A domain class is reachable on the path under its: (example Order is reachable under /orders)
- uncapitalized
- pluralized (added s)
- simple class name
- to prevent spring from exporting a repository add @RestResource(exported = false) to class or to a query method (you may overwrite them in your interface in order to annotate them)
- by default spring data rest uses HATEOS via HAL. When you call the basePath of spring data rest in a browser you get a overview of the exposed rest api.
- Paging and sorting is supported via query parameters when a PagingAndSortingRepository is used
- ?size=5
- ?page=2
- ?sort=name,desc
- custom query methods in Repository classes are exposed as well
- you can configure these with @RequestParam and @PathVariable as you would do with regular methods in a RestController
- if you want to use a @Query annotation at the same time use @Param and reference the paramname with leading colons (e.g. :date).
- if you do not configure a @PathVariable they are exposed under
<host>/<entityPlural>/search/<queryMethodName>
(they appear in the hal browser!)
- you can define generic rest query urls for a spring data repository with the Specification interface
add basic auth
@Configuration
public class WebserviceConfig {
@Value("${})
private String user;
@Value("${})
private String password;
// is used for all restTemplates! You need to name the bean if you want to call mltiple domains
@Bean
public RestTemplate myRestTemplate(RestTemplateBuilder builder) {
return builder.basicAuthorization(user, password).build();
}
}
prevent circular dependecies in generated JSON
Use @JsonManagedReference
and @JsonBackReference
see here.
Misc
General adive on spell you can: - call a static Method with spell: #{T(java.time.LocalDate).now()} - call a constructor with spell: #{new java.util Date()}