Spring REST Concepts: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
(Created page with "=Internal= * Spring MVC Concepts")
 
 
(33 intermediate revisions by the same user not shown)
Line 2: Line 2:


* [[Spring_MVC_Concepts#REST|Spring MVC Concepts]]
* [[Spring_MVC_Concepts#REST|Spring MVC Concepts]]
* [[REST and Hypermedia]]
* [[Spring_Framework#Spring_Framework_Core_Technologies_Concepts|Spring Framework]]
=Overview=
The Spring REST concepts page is an extension of the [[Spring MVC Concepts]] page. Spring MVC concepts are used and extended to provide REST support.
=Playground=
{{External|[https://github.com/ovidiuf/playground/blob/master/spring/rest/01-simplest/src/main/java/playground/spring/rest/controller/AController.java @RestController Example]}}
=Annotations=
* [[Spring_MVC_Concepts#Controller_Annotations|Spring MVC Annotations]]
* [[@RestController]]
* [[@CrossOrigin]]
=Receive Data from Client=
<font color=darkgray>TODO: [[REST and Hypermedia]]</font>
==Via Path==
===Query Parameters===
===Path Parameters===
==Via Request==
===Request Headers===
===Request Body===
====Request Body and Jackson Deserialization====
If the request body is encoded as "application/json", and declared as argument with [[@RequestBody]] as follows:
<syntaxhighlight lang='java'>
@PostMapping(consumes = "application/json")
public ... ...(@RequestBody A a) {
  ...
}
</syntaxhighlight>
Spring handles JSON deserialization transparently and instantiates the Java object representing the body. However, the class to be instantiated '''must declare a no-argument constructor''' and expose public mutators that would allow Jackson to update the state. If that is not the case, you will see something similar to:
<syntaxhighlight lang='text'>
2019-03-27 08:50:46.755  WARN 79413 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `playground.A` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `playground.A` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
</syntaxhighlight>
===Form Parameters===
=Read a Resource Representation=
Read REST resources by annotating handlers of a [[@RestController]] with [[@RequestMapping]] and [[@GetMapping]].
=Create a Resource=
Create a REST resource by  annotating handlers of a [[@RestController]] with [[@RequestMapping]], [[@PostMapping]]. POST is not safe and not idempotent.
=Update a Resource=
Update a REST resource by annotating handlers of a [[@RestController]] with [[@RequestMapping]], [[@PutMapping]] or [[@PatchMapping]]. PUT is idempotent. Also see [[#Request_Body_and_Jackson_Deserialization|Request Body and Jackson Deserialization]] above.
=Delete a Resource=
Delete a REST resource by annotating handlers of a [[@RestController]] with [[@DeleteMapping]].
=Send a Response to Client=
The [[@RestController]] annotation implies [[@ResponseBody]], which maps the result produced by the handler method onto the body of the HTTP response.
By default, if all goes well - no exceptions are thrown - the HTTP status code is 200, even if the method returns null.
If the method handler wants to control the HTTP status code, it has the option of wrapping the response in a <tt>ResponseEntity<></tt>, which, along the body, allows specifying the response code:
<syntaxhighlight lang='java'>
import org.springframework.http.ResponseEntity;
...
public ResponseEntity<A> get(...) {
  // if found ...
  return new ResponseEntity<>(a, HttpStatus.OK);
  // ... else
  return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
</syntaxhighlight>
Another way of statically enforcing a response code is with [[@ResponseStatus]].
{{Note|It is always a good idea to send a specific response code, instead of 200, where appropriate to communicate the most descriptive and accurate HTTP status to the client.}}
=REST Clients=
==<tt>RestTemplate</tt>==
<font color=darkgray> TO PROCESS: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#webmvc-resttemplate</font>
===POSTing Resource. Data===
This overloaded version allows you to receive the newly created resource as a domain model object:
<syntaxhighlight lang='java'>
RestTemplate restTemplate = new RestTemplate();
MyResource model = new MyResource(...);
MyResource created = restTemplate.postForObject("http://localhost:8080/myresource", model, MyResource.class);
</syntaxhighlight>

Latest revision as of 18:05, 13 May 2019

Internal

Overview

The Spring REST concepts page is an extension of the Spring MVC Concepts page. Spring MVC concepts are used and extended to provide REST support.

Playground

@RestController Example

Annotations

Receive Data from Client

TODO: REST and Hypermedia

Via Path

Query Parameters

Path Parameters

Via Request

Request Headers

Request Body

Request Body and Jackson Deserialization

If the request body is encoded as "application/json", and declared as argument with @RequestBody as follows:

@PostMapping(consumes = "application/json")
public ... ...(@RequestBody A a) {
  ...
}

Spring handles JSON deserialization transparently and instantiates the Java object representing the body. However, the class to be instantiated must declare a no-argument constructor and expose public mutators that would allow Jackson to update the state. If that is not the case, you will see something similar to:

2019-03-27 08:50:46.755  WARN 79413 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `playground.A` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `playground.A` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

Form Parameters

Read a Resource Representation

Read REST resources by annotating handlers of a @RestController with @RequestMapping and @GetMapping.

Create a Resource

Create a REST resource by annotating handlers of a @RestController with @RequestMapping, @PostMapping. POST is not safe and not idempotent.

Update a Resource

Update a REST resource by annotating handlers of a @RestController with @RequestMapping, @PutMapping or @PatchMapping. PUT is idempotent. Also see Request Body and Jackson Deserialization above.

Delete a Resource

Delete a REST resource by annotating handlers of a @RestController with @DeleteMapping.

Send a Response to Client

The @RestController annotation implies @ResponseBody, which maps the result produced by the handler method onto the body of the HTTP response.

By default, if all goes well - no exceptions are thrown - the HTTP status code is 200, even if the method returns null.

If the method handler wants to control the HTTP status code, it has the option of wrapping the response in a ResponseEntity<>, which, along the body, allows specifying the response code:

import org.springframework.http.ResponseEntity;
...
public ResponseEntity<A> get(...) {

  // if found ...
  return new ResponseEntity<>(a, HttpStatus.OK);

  // ... else
  return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}

Another way of statically enforcing a response code is with @ResponseStatus.


It is always a good idea to send a specific response code, instead of 200, where appropriate to communicate the most descriptive and accurate HTTP status to the client.

REST Clients

RestTemplate

TO PROCESS: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#webmvc-resttemplate

POSTing Resource. Data

This overloaded version allows you to receive the newly created resource as a domain model object:

RestTemplate restTemplate = new RestTemplate();

MyResource model = new MyResource(...);

MyResource created = restTemplate.postForObject("http://localhost:8080/myresource", model, MyResource.class);