30
Spring MVC Part 2 Spencer Uresk

Spencer Uresk

  • Upload
    veata

  • View
    42

  • Download
    0

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

Page 1: Spencer  Uresk

Spring MVC Part 2Spencer Uresk

Page 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)

Page 3: Spencer  Uresk

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

Page 4: Spencer  Uresk

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

Page 5: Spencer  Uresk

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

Page 6: Spencer  Uresk

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) {}

Page 7: Spencer  Uresk

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() {}

Page 8: Spencer  Uresk

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

Page 9: Spencer  Uresk

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

Page 10: Spencer  Uresk

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

Page 11: Spencer  Uresk

StringHttpMessageConverter

• Reads and writes Strings.• Reads text/*• Writes text/plain

Page 12: Spencer  Uresk

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

Page 13: Spencer  Uresk

MappingJacksonHttpMessageConverter

• Maps to/from JSON objects using the Jackson library

• Reads application/json• Writes application/json

Page 14: Spencer  Uresk

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

Page 15: Spencer  Uresk

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

Page 16: Spencer  Uresk

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

Page 17: Spencer  Uresk

ByteArrayHttpMessageConverter

• Can read/write byte arrays (useful for dealing with binary data, such as images)

• Reads */*• Writes application/octet-stream

Page 18: Spencer  Uresk

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

Page 19: Spencer  Uresk

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

Page 20: Spencer  Uresk

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}

Page 21: Spencer  Uresk

@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) {}

Page 22: Spencer  Uresk

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);}

Page 23: Spencer  Uresk

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.

Page 24: Spencer  Uresk

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

Page 25: Spencer  Uresk

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

Page 26: Spencer  Uresk

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");

}}

Page 27: Spencer  Uresk

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>

Page 28: Spencer  Uresk

@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();}

Page 29: Spencer  Uresk

@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 {}

Page 30: Spencer  Uresk

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