Rest: Update
parent
137bff9515
commit
c9eeedc6f6
104
Get.md
104
Get.md
@ -1,53 +1,53 @@
|
||||
`src/main/java/djmil/cashcard/CashCardController.java`
|
||||
|
||||
```java
|
||||
package example.cashcard;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class CashCardController {
|
||||
@GetMapping("/cashcards/{requestedId}")
|
||||
public ResponseEntity<CashCard> findById(@PathVariable Long requestedId) {
|
||||
if (requestedId.equals(99L)) {
|
||||
CashCard cashCard = new CashCard(99L, 123.45);
|
||||
return ResponseEntity.ok(cashCard);
|
||||
} else {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# @GetMapping
|
||||
|
||||
Needs the URI Path and tells Spring to route `GET` requests strictly to the `findById` method.
|
||||
|
||||
## Alternative variant for providing URI mapping for `@RestConttroller`
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/cashcards")
|
||||
public class CashCardController {
|
||||
|
||||
@GetMapping("/{requestedId}")
|
||||
public ResponseEntity<String> findById() {
|
||||
```
|
||||
|
||||
# @PathVariable
|
||||
|
||||
Spring needs to know how to get the value of the `requestedId` parameter. This is done using the `@PathVariable` annotation. The fact that the parameter name matches the `{requestedId}` text *(URI Path)* within the `@GetMapping` parameter allows Spring to assign (inject) the correct value to the `requestedId` variable.
|
||||
|
||||
# ResponseEntity
|
||||
|
||||
REST says that the Response needs to contain a Cash Card in its body, and a Response code of 200 (OK). Spring Web provides the `ResponseEntity` class for this purpose. It also provides several utility methods to produce Response Entities. Here `ResponseEntity` used to create a Response with code **200 (OK)**, and a body containing a `CashCard`.
|
||||
|
||||
# Testing
|
||||
|
||||
Testing `GET` and other REST request is considerate to be [[IntegrationTests]].
|
||||
|
||||
`src/main/java/djmil/cashcard/CashCardController.java`
|
||||
|
||||
```java
|
||||
package example.cashcard;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class CashCardController {
|
||||
@GetMapping("/cashcards/{requestedId}")
|
||||
public ResponseEntity<CashCard> findById(@PathVariable Long requestedId) {
|
||||
if (requestedId.equals(99L)) {
|
||||
CashCard cashCard = new CashCard(99L, 123.45);
|
||||
return ResponseEntity.ok(cashCard);
|
||||
} else {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# @GetMapping
|
||||
|
||||
Needs the URI Path and tells Spring to route `GET` requests strictly to the `findById` method.
|
||||
|
||||
## Alternative variant for providing URI mapping for `@RestConttroller`
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/cashcards")
|
||||
public class CashCardController {
|
||||
|
||||
@GetMapping("/{requestedId}")
|
||||
public ResponseEntity<String> findById() {
|
||||
```
|
||||
|
||||
# @PathVariable
|
||||
|
||||
Spring needs to know how to get the value of the `requestedId` parameter. This is done using the `@PathVariable` annotation. The fact that the parameter name matches the `{requestedId}` text *(URI Path)* within the `@GetMapping` parameter allows Spring to assign (inject) the correct value to the `requestedId` variable.
|
||||
|
||||
# ResponseEntity
|
||||
|
||||
REST says that the Response needs to contain a Cash Card in its body, and a Response code of 200 (OK). Spring Web provides the `ResponseEntity` class for this purpose. It also provides several utility methods to produce Response Entities. Here `ResponseEntity` used to create a Response with code **200 (OK)**, and a body containing a `CashCard`.
|
||||
|
||||
# Testing
|
||||
|
||||
Testing `GET` and other REST request is considerate to be [[IntegrationTests]].
|
||||
|
||||
Also, after running the application, you can use browser (or any other web-testing tool) by visiting http://localhost:8080/cashcards/99
|
91
Home.md
91
Home.md
@ -42,7 +42,7 @@ An End-to-End Test exercises the system using the same interface that a user wou
|
||||
|
||||
# RESTful API
|
||||
|
||||
REST is not exactly a standard; it’s merely a way to use HTTP to perform data operations. REST contains a number of guidelines, which developers shall follow to create coherent web-application.
|
||||
REST is not exactly a standard; it’s merely a way to use [HTTP](https://www.rfc-editor.org/rfc/rfc7231#page-21) to perform data operations. REST contains a number of guidelines, which developers shall follow to create coherent web-application.
|
||||
|
||||
In a RESTful system, data objects are called Resource Representations. The purpose of a RESTful API is to manage the state of these Resources. The chart below shows details about RESTful CRUD operations of an application.
|
||||
|
||||
@ -57,9 +57,9 @@ Another common concept associated with REST is the Hypertext Transfer Protocol.
|
||||
|
||||
## REST in Spring Boot
|
||||
|
||||
One of the main things Spring does is to configure and instantiate objects. These objects are called *Spring Beans*, and are usually created by Spring (as opposed to using the Java `new` keyword). You can direct Spring to create Beans in several ways.
|
||||
One of the main things Spring does is to configure and instantiate objects. These objects are called *Spring [Beans](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-introduction)*, and are usually created by Spring (as opposed to using the Java `new` keyword). You can direct Spring to create Beans in several ways.
|
||||
|
||||
> We will annotate a class with a `@RestController` Spring Annotation, which directs Spring to create an instance of the class during Spring’s *Component Scan* phase. This happens at application startup. The Bean is stored in Spring’s `IoC Container`. From here, the bean can be injected into any code that requests it.
|
||||
> We will annotate a class with a `@RestController` Spring Annotation, which directs Spring to create an instance of the class during Spring’s *Component Scan* phase. This happens at application startup. The Bean is stored in Spring’s [IoC Container](https://docs.spring.io/spring-framework/reference/core/beans.html). From here, the bean can be injected into any code that requests it.
|
||||
|
||||
![[Pasted image 20230719102322.png]]
|
||||
|
||||
@ -98,7 +98,7 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
## Put
|
||||
## Create
|
||||
|
||||
Our REST API can now [[Get]] Cash Cards with a specific ID. Now it's time to add the Create endpoint to the API. Four questions we’ll need to answer while doing this are:
|
||||
|
||||
@ -112,7 +112,7 @@ Our REST API can now [[Get]] Cash Cards with a specific ID. Now it's time to add
|
||||
|
||||
### Idempotence and HTTP
|
||||
|
||||
An **idempotent** operation is defined as one which, if performed more than once, results in the same outcome. In a REST API, an idempotent operation is one that even if it were to be performed several times, the resulting data on the server would be the same as if it had been performed only once.
|
||||
An **[idempotent](https://www.rfc-editor.org/rfc/rfc7231#section-4.2.2)** operation is defined as one which, if performed more than once, results in the same outcome. In a REST API, an idempotent operation is one that even if it were to be performed several times, the resulting data on the server would be the same as if it had been performed only once.
|
||||
|
||||
For each method, the HTTP standard specifies whether it is idempotent or not. `GET`, `PUT`, and `DELETE` are idempotent, whereas `POST` and `PATCH` are not.
|
||||
|
||||
@ -179,6 +179,83 @@ return ResponseEntity
|
||||
|
||||
Aren’t you glad Spring Web provides the `.created()` convenience method? Check the complete [[Post]] implementation.
|
||||
|
||||
## Update
|
||||
|
||||
Our new **Update** endpoint:
|
||||
- will use the [[Put]] verb.
|
||||
- accepts a Cash Card, and **replaces** the existing Cash Card with it.
|
||||
- on success, will return `204 NO CONTENT` with an empty body.
|
||||
- will return a `404 NOT FOUND` for an unauthorized update, as well as attempts to update nonexistent IDs.
|
||||
|
||||
Continue reading to learn why in our case `PUT` won’t support creating a Cash Card.
|
||||
|
||||
### Post, Put or Patch?
|
||||
|
||||
A next logical step is the ability to adjust a card balance! How to implement the Update operation in a RESTful API is somewhat of a hot topic, which is what we’ll tackle here. When we say “adjust the balance” on a Cash Card, what we really mean is to update the `amount` on an existing database record. Doing so will entail:
|
||||
|
||||
- Creating a new endpoint to receive an HTTP request with a verb, URI, and body
|
||||
- Returning an appropriate response from the endpoint for success and error conditions
|
||||
|
||||
We’re already familiar with the HTTP `POST` verb, which we used for the Create endpoint. Now let's talk about HTTP `PUT` and `PATCH`, and how all three of these are related.
|
||||
|
||||
#### PUT and PATCH
|
||||
|
||||
Both `PUT` and `PATCH` can be used for updating, but they work in different ways. Essentially, `PUT` means “create or replace the complete record”, whereas `PATCH` [means](https://www.rfc-editor.org/rfc/rfc5789) “update only some fields of the existing record” - in other words, a partial update.
|
||||
|
||||
Why would you want to do a partial update? Partial updates free the client from having to load the entire record and then transmit the entire record back to the server. If the record is large enough, this can have a non-trivial impact on performance.
|
||||
|
||||
> For our application, we’ll choose to **not** implement a partial update.
|
||||
|
||||
#### PUT and POST
|
||||
|
||||
The HTTP standard does not [specify](https://www.rfc-editor.org/rfc/rfc7231#page-25) whether the `POST` or `PUT` verb is preferred for a [[Home#Create]] operation! This is relevant because we’ll use the `PUT` verb for our Update endpoint, so we need to decide whether our API will support using `PUT` to either Create _or_ Update a resource.
|
||||
|
||||
There are different ways to look at the relationship between the **Create** and **Update** operations and how they are implemented in REST using HTTP verbs. The important takeaway from the following sections is not to memorize all the details, but simply to realise that there are lots of different choices, and that you, as Cash Card API author, are consciously making decisions about how to implement REST.
|
||||
|
||||
### Surrogate and Natural Keys
|
||||
|
||||
Why would we want to use a `PUT` operation to create a resource? This has to do with the HTTP definition of the two verbs. The difference is subtle. Let’s explain it by comparing two different systems: Our **Cash Card API**, and another API that we'll introduce for explanatory purposes, called the **Invoice API**. The Invoice API accepts the Invoice Number as the unique identifier. This is an example of using a **Natural Key** (supplied by the client to the API) instead of a **Surrogate Key** (usually generated by the server, which is what we are doing in our Cash Card API).
|
||||
|
||||
The important difference is **whether the URI (which includes the ID of the resource) needs to be generated by the server** or not. Here is how PUT and POST think about it:
|
||||
|
||||
> If you need the server to return the URI of the created resource (or the data you use to construct the URI), then you must use `POST`.
|
||||
|
||||
This is the case for our Cash Card API: To create a Cash Card, we provide the `POST /cashcards` endpoint. The actual URI for the created Cash Card depends on the generated ID, and is provided by the server, for example, `/cashcards/101` if the ID of the created card is 101.
|
||||
|
||||
> Alternatively, when the resource URI is known at creation time (as is the case in our example Invoice API), you can use `PUT`.
|
||||
|
||||
For the Invoice API, we could write a Create endpoint that accepts requests such as `PUT /invoice/1234-567`. The corresponding Read call would use the exact same URI: `GET /invoice/1234-567`.
|
||||
|
||||
### Resources and Sub-Resources
|
||||
|
||||
Another way to look at the difference is in terms of **URIs and collections of sub-resources**. This is the language used by the HTTP documentation, so it's good to be familiar with it. Following the above examples, we’d find:
|
||||
|
||||
- **`POST` creates a sub-resource (child resource) _under (after)_, or _within_ the request URI**. This is what the Cash Card API does: The client calls the Create endpoint at `POST /cashcards`, but the actual URI of the created resource contains a generated ID at the end: `/cashcards/101`
|
||||
- **`PUT` creates or replaces (updates) a resource _at a specific_ request URI**. For the `/invoice` example above, the Create endpoint would be `PUT /invoice/1234-567`, and the URI of the created resource would be the same as the URI sent in the `PUT` request.
|
||||
|
||||
### Response Body and Status Code
|
||||
|
||||
Related to deciding whether to allow `PUT` to create objects, you need to decide what the response status code and body should be. Two different options are:
|
||||
|
||||
> Return `201 CREATED` (if you created the object), or `200 OK` (if you replaced an existing object). In this case it's recommended to return the object in the response body. This is useful if data was added to the object by the server (for example, if the server records the creation date).
|
||||
|
||||
_or_
|
||||
|
||||
> Return `204 NO CONTENT`, and an empty response body. The rationale in this case is that since a `PUT` simply places on object at the URI in the request, the client doesn't need any information back - it knows that the object in the request has been saved, verbatim, on the server.
|
||||
|
||||
### Summary
|
||||
|
||||
|HTTP Method|Operation|Definition of Resource URI|What does it do?|Response Status Code|Response Body|
|
||||
|---|---|---|---|---|---|
|
||||
|**`POST`**|**Create**|**Server generates and returns the URI**|**Creates a sub-resource ("under" or "within" the passed URI)**|**`201 CREATED`**|**The created resource**|
|
||||
|`PUT`|Create|Client supplies the URI|Creates a resource (at the Request URI)|`201 CREATED`|The created resource|
|
||||
|**`PUT`**|**Update**|**Client supplies the URI**|**Replaces the resource: The entire record is replaced by the object in the Request**|**`204 NO CONTENT`**|**(empty)**|
|
||||
|`PATCH`|Update|Client supplies the URI|Partial Update: modify only fields included in the request on the existing record|`200 OK`|The updated resource|
|
||||
|
||||
In the Cash Card API, we don’t need to allow `PUT` to create resources. We also have no need to add data on the server side for an Update operation, nor do we need to allow for partial update. So, our `PUT` endpoint is limited to row 3 of the above table.
|
||||
|
||||
The **bold** rows in the above table are implemented by Cash Card API. The non-bold ones are not.
|
||||
|
||||
# Database
|
||||
|
||||
The [**Separation of Concerns**](https://en.wikipedia.org/wiki/Separation_of_concerns) principle states that well-designed software should be modular, with each module having distinct and separate concerns from any other module.
|
||||
@ -292,7 +369,7 @@ extends
|
||||
|
||||
But how do we get query configuration data for sorting?
|
||||
|
||||
## The URI
|
||||
## The URL
|
||||
|
||||
The HTTP request parameters is used to transfer values that is used to configure pagination query: *(we've omitted the `https://domain` prefix in the following)*
|
||||
|
||||
@ -336,7 +413,7 @@ SOP is critical to the security of web sites because without the policy, anyone
|
||||
|
||||
### Cross-Origin Resource Sharing
|
||||
|
||||
Sometimes a system consists of services running on several machines with different URIs (i.e. Microservices). **Cross-Origin Resource Sharing (CORS)** is a way that browsers and servers can cooperate to relax the SOP. A server can explicitly allow a list of “allowed origins” of requests coming from an origin outside the server’s.
|
||||
Sometimes a system consists of services running on several machines with different URIs (i.e. Microservices). **Cross-Origin Resource Sharing ([CORS](https://docs.spring.io/spring-framework/reference/web/webmvc-cors.html))** is a way that browsers and servers can cooperate to relax the SOP. A server can explicitly allow a list of “allowed origins” of requests coming from an origin outside the server’s.
|
||||
|
||||
Spring Security provides the `@CrossOrigin` annotation, allowing you to specify a list of allowed sites. Be careful! If you use the annotation _without_ any arguments, it will allow _all_ origins, so bear this in mind!
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user