1 Learn the basics of REST1.1 rest
Rest has little to do with RPC. RPC is service-oriented and focuses on behavior and actions, while rest is resource-oriented, emphasizing the things and nouns that describe the application.
To understand what rest is, we split its initials into different components:
- Presentation (representational): rest resources can actually be expressed in a variety of forms, including XML, JSON (JavaScript Object Notation), or even html--, which is best suited to any form of resource user;
- State: When using rest, we pay more attention to the state of the resource than to the behavior of the resource;
- Transfer (Transfer): Rest involves transferring resource data, which is transferred from one application to another in some form of representation.
There are behaviors in rest that are defined by the HTTP method. Specifically, the GET, POST, PUT, DELETE, Patch, and other HTTP methods make up the actions in rest. These HTTP methods are typically matched to the following crud actions:
- Create:post
- Read:get
- Update:put or patch
- Delete:delete
1.2 How spring is supported for rest
In the current 4.0 release, spring supports the following ways to create rest resources:
- The controller can handle all HTTP methods, including four main rest methods: GET, PUT, delete, and post. Spring 3.2 and later also supports patch methods;
- With @pathvariable annotations, the controller is able to handle parameterized URLs (variable input as part of the URL);
- With spring view and view resolvers, resources can be expressed in a variety of ways, including rendering model data as XML, JSON, Atom, and RSS view implementations;
- You can use Contentnegotiatingviewresolver to select the best representation for the client;
- Can replace view-based rendering with @responsebody annotations and various httpmethodconverter implementations;
- Similarly, @RequestBody annotations and Httpmethodconverter implementations can convert incoming HTTP data into Java objects that pass through the controller's processing methods;
- With resttemplate,spring apps, you can easily use rest resources.
2 Creating the first rest endpoint
Spring provides two ways to convert the Java representation of a resource into a representation that is sent to the client:
- Content negotiation: Select a view that renders the model as a representation of the presentation to the client;
- Message conversion: Converts the object returned by the controller to a representation presented to the client through a message converter.
2.1 Negotiating Resource representations
In a Web application for human access, the selected view is typically rendered as HTML. The view resolution scheme is a simple one-dimensional activity. If the view is matched according to the view name, then this is the view we are going to use.
When you want to resolve the view name to a view that produces a resource representation, we have another dimension to consider. The view will not only match the view name, but the selected view will be appropriate for the client. If the client wants JSON, the rendered HTML view is not.
Spring's contentnegotiatingviewresolver is a special view parser that takes into account the type of content the client needs.
To understand how Contentnegotiatingviewresolver works, this involves two steps of content negotiation:
- 1. Determine the requested media type;
- 2. Find the best view for the requested media type.
determine the requested media type
Contentnegotiatingviewresolver will take into account the Accept header information and use the media type it requests, but it will first look at the URL's file name extension. If the URL has a file extension at the end, Contentnegotiatingviewresolver will determine the desired type based on the extension. If the extension is ". JSON", then the required content type must be "Application/json". If the extension is ". xml", then the client is requesting "Application/xml". Of course, the ". html" extension indicates that the resource required by the client is expressed as HTML (text/html).
If no media type is available based on the file name extension, the Accept header information in the request is considered. In this case, the value in the Accept header message indicates the MIME type that the client wants, and there is no need to look for it.
Finally, if there is no accept header information, and the extension does not help, Contentnegotiatingviewresolver will use "/" as the default content type, which means that the client must receive any form of representation sent by the server.
Once the content type is determined, Contentnegotiatingviewresolver should resolve the logical view name to the view of the rendered model. Unlike other view parsers in spring, the contentnegotiatingviewresolver itself does not parse the view. Instead, delegate to other view parsers to parse the view.
choices that affect media types
In the previous selection process, we described the default policy for determining the type of media being requested. But by setting a contentnegotiationmanager for it, we can change its behavior. The things we can do with Content-negotiationmanager are as follows:
- Specifies the default content type, which will be used if the content type cannot be obtained based on the request;
- Specify the content type by the request parameter;
- Ignoring the requested accept header information;
- Map the requested extension to a specific media type;
- Use the JAF (Java Activation Framework) as an alternative to finding the media type based on the extension.
There are three ways to configure Contentnegotiationmanager:
- Declare a bean of the contentnegotiationmanager type directly;
- Creating the bean indirectly through Contentnegotiationmanagerfactorybean;
- The Configurecontentnegotiation () method of the overloaded Webmvcconfigureradapter.
Creating Contentnegotiationmanager directly is somewhat complex, and we will not be willing to do so unless there are sufficient reasons. The latter two scenarios make it easier to create contentnegotiationmanager.
- Configuring Contentnegotiationmanager with XML
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean" p:defaultContentType ="application/json"></bean>
- Configuring Configurecontentnegotiation with Java
@Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.defaultContentType(MediaType.APPLICATION_JSON); }
Now that we have the contentnegotiationmanagerbean, we need to inject it into the Contentnegotiatingviewresolver Contentnegotiationmanager attribute.
@Bean public ViewResolver cnViewResolver(ContentNegotiationManager cnm) { ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver(); cnvr.setContentNegotiationManager(cnm); return cnvr; }
The following list of programs is a very simple configuration sample, which is usually used when I use Contentnegotiatingviewresolver: it uses the HTML view by default, but the specific view name will be rendered as JSON output.
If the name of the logical view is "Spittles", then the beannameviewresolver that we configured will parse the view declared in the Spittles () method. This is because the bean name matches the name of the logical view. If there is no matching view, Contentnegotiatingviewresolver will use the default behavior to output it as HTML.
advantages and limitations of Contentnegotiatingviewresolver
The biggest advantage of Contentnegotiatingviewresolver is that it builds a rest resource presentation layer on top of spring MVC, and the controller code does not need to be modified. The same set of controller methods can generate HTML content for human-facing users, and can generate JSON or XML for clients that are not human.
Contentnegotiatingviewresolver also has a related minor problem, the selected view renders the model to the client instead of the resource. Here is a subtle but important distinction. When a client requests a JSON-formatted list of Spittle objects, the client expects the response to be as follows:
Because of these limitations, I usually recommend that you do not use Contentnegotiatingviewresolver. I'm more inclined to use the Spring Message transformation feature to generate a resource table
Described Next, let's look at how to use the Spring message Converter in the controller code.
2.2 Using the HTTP information converter
Message conversion provides a more straightforward way to transform the data generated by the controller into a representation that serves the client. When using the Message transformation feature, Dispatcherservlet no longer needs to be so cumbersome to transfer model data into the view. In fact, there is no model at all, there is no view, only the data generated by the controller, as well as the resource representations generated after the message converter transformed the data.
Table 16.1 Spring provides multiple HTTP information converters for translating resource representations to and from various Java types
return the resource status in the response body
Normally, when a processing method returns a Java object (other than string or view implementation), the object is placed in the model and rendered in the view. However, if you use the Message transformation feature, we need to tell spring to skip the normal model/view process and use the message converter. There are many ways to do this, but the simplest way is to add @responsebody annotations to the controller method.
@ResponseBody note tells Spring that we want to send the returned object as a resource to the client and convert it to an acceptable representation of the client. More specifically, Dispatcherservlet will take into account the Accept header information in the request and look for a message converter that provides the client with the required representation. The message converter converts the list of spittle returned by the controller to a JSON document and writes it to the response body. The response will roughly resemble the following:
@RequestMapping(method=RequestMethod.GET, produces = "application/json") public @ResponseBody List<Spittle> spittles( @RequestParam(value="max", defaultValue=MAX_LONG_AS_STRING) long max, @RequestParam(value="count", defaultValue="20") int count) { return spittleRepository.findSpittles(max, count); }
When talking about the accept header, please note the @requestmapping annotation of Getspitter (). Here, I used the produces property to indicate that this method only handles requests that are expected to output as JSON. In other words, this method only handles requests for the Accept header message containing "Application/json". Any other type of request, even if its URL matches the specified path and is a GET request, will not be processed by this method. Such requests are handled by other methods (if appropriate), or the client HTTP 406 (not acceptable) response is returned.
Receive resource status in the request body
@ResponseBody can tell spring to use a particular message when sending the data to the client, @RequestBody can also tell spring to look for a message converter to convert the resource representation from the client to an object. For example, suppose we need a way to save the new spittle that the client submits. We can write a controller method to handle this request in the following way:
@RequestMapping(method=RequestMethod.POST,consumes = "application/json") public @ResponseBody Spittle saveSpittle(@RequestBody Spittle spittle) { return spittleRepository.save(spittle); }
@RequestMapping indicates that it can only handle post requests for "/spittles" (declared in @requestmapping at the class level).
@RequestMapping has a consumes property, we set it to "Application/json". The consumes property works like produces, but it focuses on the requested Content-type header information. It tells Spring that this method will only handle post requests for "/spittles" and that the requested Content-type header information is "Application/json".
set message conversions for the controller by default
Spring 4.0 introduces @restcontroller annotations to help us with this. If you use @restcontroller on the controller class instead of @controller, spring will apply the message transformation functionality for all of the controller's processing methods. We don't have to add @responsebody for each method.
3 provide content other than resources 3.1 send error message to client
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public @ResponseBody Spittle spittleById(@PathVariable("spittleId") long spittleId, { return spittleRepository.findOne(spittleId); }
What do you think will happen if the ID property of a Spittle object cannot be found according to the given ID, and the FindOne () method returns null? The result is that the Spittlebyid () method returns NULL, the response body is empty, and no useful data is returned to the client. At the same time, the default HTTP status code in the response is (OK), indicating that everything is working properly.
Now, let's consider what should happen in this scenario. At a minimum, the status code should not be 200, but should be 404 (not Found), telling the client that the content they requested was not found. It would be better if the response body could contain an error message instead of being empty.
Spring provides a variety of ways to handle such scenarios:
- Use @responsestatus annotations to specify status codes;
- The Controller method can return a Responseentity object that can contain more response-related metadata;
- The exception handler is able to handle the error scenario so that the processor approach can focus on the normal situation.
using responseentity
As an alternative to @responsebody, the Controller method can return a Responseentity object. Responseentity can include response-related metadata, such as header information and status codes, as well as objects to be converted into resource representations.
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public ResponseEntity<Spittle> spittle(@PathVariable("spittleId") long spittleId, { Spittle spittle = spittleRepository.findOne(spittleId); HttpStatus status = spittle != null ? HttpStatus.OK : HttpStatus.NOT_FOUND; return new ResponseEntity<Spittle>(spittle, status); }
Handling Errors
The IF code block in the Spittlebyid () method is handled incorrectly, but this is the area where the controller's error handler is adept. The error handler is able to handle the scenario that caused the problem, so that the conventional processor approach can only care about the normal logical processing path.
Let's refactor the code to use the error handler. First, define the error handler that corresponds to the spittlenotfoundexception:
@ExceptionHandler(SpittleNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public @ResponseBody Error spittleNotFound(SpittleNotFoundException e) { long spittleId = e.getSpittleId(); return new Error(4, "Spittle [" + spittleId + "] not found"); }
The classes involved are:
public class Error { private int code; private String message; public Error(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; }}
public class SpittleNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L; private long spittleId; public SpittleNotFoundException(long spittleId) { this.spittleId = spittleId; } public long getSpittleId() { return spittleId; }}
We can remove most of the error handling code from the Spittlebyid () method:
@RequestMapping(value="/{id}", method=RequestMethod.GET, produces="application/json") public Spittle spittleById(@PathVariable Long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { throw new SpittleNotFoundException(id); } return spittle; }
Above, now that we know Spittlebyid () will return spittle and the HTTP status code will always be (OK), then you can no longer use responseentity, but instead replace it with @responsebody. If @restcontroller is used on the controller class, we no longer need @responsebody.
3.2 Setting header information in response
The client knows the newly created resource, do you think the client will be interested in where the newly created resource is?
When creating a new resource, it is a good way to place the URL of the resource in the location header of the response and return it to the client. So we need a way to fill in the response header information, and our old friend Responseentity can help.
The following list of programs shows a new version of Savespittle (), which returns responseentity used to tell the client about the newly created resource.
@RequestMapping(method=RequestMethod.POST, consumes="application/json") @ResponseStatus(HttpStatus.CREATED) public ResponseEntity<Spittle> saveSpittle(@RequestBody Spittle spittle, UriComponentsBuilder ucb) { Spittle saved = spittleRepository.save(spittle); HttpHeaders headers = new HttpHeaders(); URI locationUri = ucb.path("/spittles/") .path(String.valueOf(saved.getId())) .build() .toUri(); headers.setLocation(locationUri); ResponseEntity<Spittle> responseEntity = new ResponseEntity<Spittle>(saved, headers, HttpStatus.CREATED); return responseEntity; }
Known information such as host, port, and servlet content are preconfigured in the Uricomponentsbuilder obtained by the processor method.
Note that the build of the path is divided into two steps. The first step is to call the path () method and set it to "/spittles/", which is the base path that the controller can handle. Then, when you call path () for the second time, the ID of the saved spittle is used. We can infer that each call to path () will be based on the result of the last call. After the path setting is complete, the build () method is called to build the UriComponents object, and the URI of the newly created spittle is obtained by invoking Touri () on the object.
4 Writing the rest Client 4.1 understanding resttemplate Operations
Resttemplate defines 36 methods for interacting with rest resources, most of which correspond to HTTP methods. However, I do not have enough space in this chapter to cover all 36 methods.
In addition to trace, Resttemplate covers all HTTP actions. In addition, execute () and Exchange () provide a lower-level generic method to use any HTTP method.
Most of the operations in table 16.2 are overloaded in the form of three methods:
- One uses Java.net.URI as the URL format and does not support parameterized URLs;
- One uses string as the URL format and uses map to indicate URL parameters;
- One uses string as the URL format and uses a mutable argument list to indicate the URL parameter.
Table 16.2 Resttemplate defines 11 separate operations, each of which has an overload, which is a total of 36 methods
4.2 Get Resources
Let's take a look at the slightly simpler Getforobject () method first. Then look at how to use the Getforentity () method to get more information from the get response.
4.3 Retrieving resources
4.4 Extracting metadata for the response
4.5 Put resources
4.6 Delete Resource
4.7 Post Resource data
Postforobject () and postforentity () handle post requests in a manner similar to the Getforobject () and getforentity () methods that send get requests. Another method is Getforlocation (), which is unique to the POST request.
4.8 Getting the Response object in the POST request
4.9 Get the resource location after the POST request
4.10 Exchanging Resources
Different from the getforentity ()-or Getforobject ()--exchange () method allows header information to be set in the request.
If you do not specify the header information, Exchange () Get requests for Spitter will have the following header information:
Suppose we want the server to send resources in JSON format. In this case, we need to set "Application/json" to the unique value of the Accept header information. Setting the request header information is simple, just construct the Httpentity object that is sent to the Exchange () method, and Httpentity contains the Multivaluemap that hosts the header information:
Now we can pass in Httpentity to invoke Exchange ():
Source
Https://github.com/myitroad/spring-in-action-4/tree/master/Chapter_16
List of attachments
- Cnm.jpg
- Converter1.jpg
- Converter2.jpg
- Converterslt.jpg
- Defheader.jpg
- Delrsc.jpg
- Exchange.jpg
- Getrscentity.jpg
- Getrscobject.jpg
- Postforentity.jpg
- Postforloc.jpg
- Postforobj.jpg
- Putrsc.jpg
- Resp1.jpg
- Resp2.jpg
- Resttempoper1.jpg
- Resttempoper2.jpg
- Setheader.jpg
16th-Creating a rest API using spring MVC