Upload
veata
View
42
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Spring MVC Part 2. Spencer Uresk. Notes. This is a training, NOT a presentation Please ask questions This is being recorded https://tech.lds.org/wiki/Java_Stack_Training Prerequisites Beginning Spring MVC (and all of its prerequisites). Overview. - PowerPoint PPT Presentation
Citation preview
Spring MVC Part 2Spencer Uresk
Notes
• This is a training, NOT a presentation• Please ask questions• This is being recorded• https://tech.lds.org/wiki/Java_Stack_Training• Prerequisites
– Beginning Spring MVC (and all of its prerequisites)
Overview
• Last time, we showed how to map requests to handler methods, get information about the request, and how to pass information back to the view
• We’ll see what an HTTP message looks like• This week, we’ll look at some of Spring MVC’s RESTful
features, including RequestBody, ResponseBody, HttpMessageConverters, HttpEntity objects, and dealing with exceptions
• These are useful for RESTful web services and normal form-based interactions
HTTP Message
• What does an HTTP message look like?• Sample Requests:GET /view/1 HTTP/1.1User-Agent: ChromeAccept: application/json[CRLF]
POST /save HTTP/1.1User-Agent: IEContent-Type: application/x-www-form-urlencoded[CRLF]name=x&id=2
Request Line
Headers
Request Line
Headers
Request Body
HTTP Message (Responses)
• Sample ResponsesHTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 1337[CRLF]<html>Some HTML Content.</html>
HTTP/1.1 500 Internal Server Error
HTTP/1.1 201 CreatedLocation: /view/7[CRLF]Some message goes here.
Status Line
Headers
Response Body
Status Line
Status LineHeaders
Response Body
RequestBody
• Annotating a handler method parameter with @RequestBody will bind that parameter to the request body
@RequestMapping("/echo/string")public void writeString(@RequestBody String input) {}
@RequestMapping("/echo/json")public void writeJson(@RequestBody SomeObject input) {}
ResponseBody
• Annotating a return type with @ResponseBody tells Spring MVC that the object returned should be treated as the response body
• No view is rendered in this case @RequestMapping("/echo/string")public @ResponseBody String readString() {}
@RequestMapping("/echo/json")public @ResponseBody SomeObject readJson() {}
HttpMessageConverters
• How does Spring MVC know how to turn a JSON string into SomeObject, or vice-versa?
• HttpMessageConverters• These are responsible for converting a request body to a
certain type, or a certain type into a response body• Spring MVC figures out which converter to use based on
Accept and Content-Type headers, and the Java type• Your Accept and Content-Type headers DON’T have to
match. For example, you can send in JSON and ask for XML back
HttpMessageConverters
• A number of HttpMessageConverters are already provided
• You can define your own, but that is outside the scope of this training
• You don’t specify which ones are used to convert request/response bodies – they are selected based on the Content-Type/Accept headers
MIME Types
• HttpMessageConverters make heavy use of MIME types (RFC 2046)
• These are the value for Accept and Content-Type headers
• Two-part identifier for content formats• First part is the type. ie, application• Second part is the sub-type. ie, json• application/json
StringHttpMessageConverter
• Reads and writes Strings.• Reads text/*• Writes text/plain
StringHttpMessageConverter
• a
POST /echo/string HTTP/1.1Accept: text/plainContent-Type: text/plain
Hello!
@RequestMapping("/echo/string")public @ResponseBody String echoString(@RequestBody String input) {
return “Your Text Was: “ + input;}
HTTP/1.1 200 OKContent-Type: text/plainContent-Length: 17
Your Text Was: Hello!
REQUEST
RESPONSE
MappingJacksonHttpMessageConverter
• Maps to/from JSON objects using the Jackson library
• Reads application/json• Writes application/json
MappingJacksonHttpMessageConverter
• a
POST /echo/string HTTP/1.1Accept: application/jsonContent-Type: application/json
{ “name” : “Spencer”, “age” : 5 }
public Person { // String name, int age;}
@RequestMapping("/echo/json")public @ResponseBody Person echoJson(@RequestBody Person person) {
// Upper case name, square agereturn person;
}
HTTP/1.1 201 CreatedContent-Type: application/json
{ “name” : “SPENCER”, “age” : 25 }
REQUEST
RESPONSE
Jaxb2RootElementHttpMessageConverter
• Maps to/from XML objects• Must have your object at least annotated with
@XmlRootElement• Reads text/xml, application/xml• Writes text/xml, application/xml
Jaxb2RootElementHttpMessageConverter
• a
POST /echo/string HTTP/1.1Accept: application/xmlContent-Type: application/xml
<thing><name>Spencer</name><age>5</age></thing>
@XmlRootElementpublic Person {// String name, int age;}
@RequestMapping("/echo/xml")public @ResponseBody Person echoXml(@RequestBody Person person) {
// Upper case name, square agereturn person;
}
HTTP/1.1 201 CreatedContent-Type: application/xml
<thing><name>SPENCER</name><age>25</age></thing>
REQUEST
RESPONSE
ByteArrayHttpMessageConverter
• Can read/write byte arrays (useful for dealing with binary data, such as images)
• Reads */*• Writes application/octet-stream
ByteArrayHttpMessageConverter
• a
POST /echo/string HTTP/1.1Accept: text/plainContent-Type: text/plain
Hello!
@RequestMapping("/echo/string")public @ResponseBody String echoString(@RequestBody byte[] input) {
return new String(input);}
HTTP/1.1 200 OKContent-Type: application/octet-streamContent-Length: 6
Hello!
REQUEST
RESPONSE
Lab 1
• Create a handler that takes a request body and echoes it back.
• Create a handler that takes a request body, creates an object with it, and returns it as JSON.
• Create a handler that takes an XML input and echoes it back as JSON.
• Test all of these with your HttpClient
Other parts of the HTTP Message
• What if you need to get/set headers?• Or set the status code?@RequestMapping("/echo/string")public @ResponseBody String echoString(@RequestBody String input,
HttpServletRequest request,HttpServletResponse response) {
String requestType = request.getHeader(“Content-Type”);response.setHeader(“Content-Type”, “text/plain”);
response.setStatus(200);
return input}
@ResponseStatus
• There is a convenient way to set what the default status for a particular handler should be
• @ResponseStatus
@RequestMapping("/create")@ResponseStatus(HttpStatus.CREATED) // CREATED == 201public void echoString(String input) {}
HttpEntity
• Convenience class for dealing with bodies, headers, and status
• Converts messages with HttpMessageConverters
@RequestMapping("/image/upload")public ResponseEntity<String> upload(HttpEntity<byte[]> rEntity) {
String t = rEntity.getHeaders().getFirst(“Content-Type”);byte[] data = rEntity.getBody();// Save the fileHttpHeaders responseHeaders = new HttpHeaders();responseHeaders.set(“Location”, “/image/1”);
return new ResponseEntity<String>(“Created”, responseHeaders, HttpStatus.CREATED);}
Lab 2
• Convert all your String controller method to use HttpEntity
• Convert the Create Person controller method to use an HttpEntity. Also, return a Location header and a 201 (Created) response code.
Dealing with exceptions
• By default, Spring MVC will map certain exceptions to status codes
• You can implement your own HandlerExceptionResolver, which takes an exception and returns a ModelAndView
• You can register a SimpleMappingExceptionResolver to map exceptions to views
• You can annotate methods in the controller to handle specific exceptions
Default Exception Mappings
• These take effect if you have no other configuration
• ConversionNotSupportedException => 500• NoSuchMethodHandlingException => 404• MissingServletRequestParameterException => 400• HttpRequestMethodNotSupportedException => 405• TypeMismatchException => 400• HttpMediaTypeNotSupportedException => 415• HttpMediaTypeNotAcceptableException => 406
HandlerExceptionResolver
• Allows you to control how exceptions are resolved• Implement HandlerExceptionResolver (but you’ll
probably extend AbstractHandlerExceptionResolver)class AnExceptionHandler extends AbstractHandlerExceptionResolver {
protected ModelAndView doResolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) {
System.out.println("I got an error.");return new ModelAndView("errors/someError");
}}
SimpleMappingExceptionResolver
• Allows you to simply map exceptions to views• This is how the Stack comes configured
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">errors/dataAccessFailure</prop> <prop key=".AccessDeniedException">errors/dataAccessFailure</prop> <prop key=".TypeMismatchException">errors/resourceNotFound</prop> </props> </property> <property name="defaultErrorView" value="errors/generalError"/> <property name="warnLogCategory" value="org.lds.stack"/></bean>
@ExceptionHandler
• Create a method to handle the exception, annotate it with @ExceptionHandler, and pass in the exception(s) that method can handle
• ExceptionHandler methods look a lot like normal handler methods
@RequestMapping("/error")public void doSomething() { throw new RecoverableDataAccessException("Unable to access that database.");}
@ExceptionHandler(DataAccessException.class)public @ResponseBody String handleDataAccessError(DataAccessException ex) { return ex.getMessage();}
@ResponseStatus
• We saw this annotation earlier• It can also be placed on Exception classes or
@ExeptionHandler methods to return a specific status code for a particular exception
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ExceptionHandler(DataAccessException.class)public void handleDataAccessError(DataAccessException ex) {}
@ResponseStatus(value = HttpStatus.PAYMENT_REQUIRED, message = “I need money.”)public class PaymentRequiredException {}
Lab 3
• Look at the SimpleMappingExceptionResolver already configured in your project
• Create a controller that throws one of those exceptions and verify that your request gets redirected to the corresponding view
• Remove the config, and change your exception to HttpMediaTypeNotSupportedException. Verify that you get a 415 using your Http Client
• Implement an @ExceptionHandler method