Test the HTTP Post Endpoint
Edit src/test/java/example/cashcard/CashCardApplicationTests.java
and add the following test method.
import java.net.URI;
...
@Test
void shouldCreateANewCashCard() {
// The database will create and manage all unique `CashCard.id` values for us. We should not provide one.
CashCard newCashCard = new CashCard(null, 250.00);
// Post request MUST provide newCashCard data
// We expect nothing (Void) in the Post.Response.Body
ResponseEntity<Void> createResponse = restTemplate.postForEntity("/cashcards", newCashCard, Void.class );
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Get Location Header of newly created respurce and use it for Get request
URI locationOfNewCashCard = createResponse.getHeaders().getLocation();
ResponseEntity<String> getResponse = restTemplate.getForEntity(locationOfNewCashCard, String.class);
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
// Validate Get response - a JSON for newly created CashCard entity
DocumentContext documentContext = JsonPath.parse(getResponse.getBody());
Number id = documentContext.read("$.id");
Double amount = documentContext.read("$.amount");
assertThat(id).isNotNull();
assertThat(amount).isEqualTo(250.00);
}
Location
Header
The official spec continue to state the following:
send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created ...
In other words, when a POST
request results in the successful creation of a resource, such as a new CashCard
, the response should include information for how to retrieve that resource. We'll do this by supplying a URI in a Response Header named "Location".
URI locationOfNewCashCard = createResponse.getHeaders().getLocation();
Note that URI is indeed the correct entity here and not a URL; a URL is a type of URI, while a URI is more generic.
Simple endpoint stub
The POST
endpoint is similar to the GET
endpoint in our CashCardController
, but uses the @PostMapping
annotation from Spring Web. The POST
endpoint must accept the data we are submitting for our new CashCard
, specifically the amount
. But what happens if we don't accept the CashCard
?
Edit src/main/java/example/cashcard/CashCardController.java
and add the following method.
import org.springframework.web.bind.annotation.PostMapping;
...
@PostMapping
private ResponseEntity createCashCard() {
return null;
}
Note that by returning nothing at all, Spring Web will automatically generate an HTTP Response Status code of 200 OK
. But, this isn't very satisfying -- our POST
endpoint does nothing! So let's make our tests better.
Actual implementation
import org.springframework.web.util.UriComponentsBuilder;
...
@PostMapping
private ResponseEntity<Void> createCashCard(@RequestBody CashCard newCashCardRequest, UriComponentsBuilder ucb) {
// CrudRepository.save - Create
CashCard savedCashCard = cashCardRepository.save(newCashCardRequest);
URI locationOfNewCashCard = ucb
.path("cashcards/{id}")
.buildAndExpand(savedCashCard.id())
.toUri();
return ResponseEntity.created(locationOfNewCashCard).build();
}
CrudRepository.save
CashCard savedCashCard = cashCardRepository.save(newCashCardRequest);
CrudRepository
provides methods that support creating, reading, updating, and deleting data from a data store. cashCardRepository.save(newCashCardRequest)
does just as it says: it saves a new CashCard
for us, and returns the saved object with a unique id
provided by the database. Amazing!
Post request, get the body
createCashCard(@RequestBody CashCard newCashCardRequest, ...)
Unlike the GET
we added earlier, the POST
expects a request "body". This contains the data submitted to the API. Spring Web will deserialize the data into a CashCard
for us.
Location
Header
URI locationOfNewCashCard = ucb
.path("cashcards/{id}")
.buildAndExpand(savedCashCard.id())
.toUri();
This is constructing a URI to the newly created CashCard
. This is the URI that the caller can then use to GET the newly-created CashCard
. Note that savedCashCard.id
is used as the identifier, which matches the GET
endpoint's specification of cashcards/<CashCard.id>
.
Where did
UriComponentsBuilder
come from?
We were able to add UriComponentsBuilder ucb
as a method argument to this POST
handler method and it was automatically passed in. How so? It was injected from our now-familiar friend, Spring's IoC Container. Thanks, Spring Web!
Security
It is pretty obvious, that the system should have some way of handling data from different users. The Home#Security section is a good place to continue reading. You can continue explore other CRUD methods afterwards.