Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Spring MVC 3 Restful

27,598 views

Published on

Spring MVC 3 Restful

Published in: Technology, News & Politics
  • Be the first to comment

Spring MVC 3 Restful

  1. 1. Spring 3 MVC Rest (3.0.5 기준)<br />김용환<br />Knight76 at gmail.com<br />Knight76.tistory.com<br />맛보기<br />
  2. 2. 특징<br />REST를 쓰기 위해서 Spring MVC 모델을 그대로 차용, Annotation 이용(Controller)<br />JSR 311을 따르지는 않지만, 대부분의 기능 구현<br />하나의 리소스는 여러 개의 Represenation을 가질 수있도록함 (JSON/XML/ATOM/RSS)<br />브라우저에서 지원하지 않는 PUT & POST 요청을 처리할 수 있음<br />Custom parser 이용 가능<br />UTIL(converter..) 클래스 지원<br />=> REST 관련 Conception만 조금 공부하면 되고, 나머지는 기존 MVC만 알면 되기 까닭에재사용성이 큼<br />
  3. 3. Spring 3 Rest 지원#1<br />Annotation 지원 <br />@Controller : MVC<br />@RequestMapping : HTTP 메소드, URI, 헤더 처리@RequestMapping(method=RequestMethod.GET, value="/members", <br />@PathVariable: 메소드 안에서 파라미터와매핑하기 위해서 사용public ModelAndViewgetEmployee(@PathVariable String id) { … } <br />@RequestParam : URL 매개변수 이용<br />@RequestHeader<br />@RequestBody<br />@HttpEntity<T><br />@ResponseEntity<T> : 정의한대로 response 리턴<br />public @ResponseBody Employee getEmployeeBy(@RequestParam("name") String name, @RequestHeader("Accept") String accept, @RequestBody String body) {…} <br />public ResponseEntity<String> method(HttpEntity<String> entity) {…} <br />
  4. 4. Spring 3 Rest 지원 #1<br />Annotation 지원<br />@ResponseStatus<br />@ExceptionHandler<br />
  5. 5. Spring 3 Rest 지원 #2<br />ContentNegotiatingViewResolver<br />요청 데이터 포맷에 맞춰 다양한 MIME(미디어 타입) 이나 content type으로 전달 가능<br />ATOM, RSS, XML, JSON, OXM<br />예)<br />http://localhost:8080/fruit/banana.xml<br />http://localhost:8080/fruit/banana.rss<br />http://localhost:8080/fruit/banana.html<br />Accept : application/xml<br />Accept : application/json<br />Accept : application/html<br />
  6. 6. 좋은자료<br />http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html<br />http://dev.anyframejava.org/docs/anyframe/plugin/restweb/1.0.1/reference/html/index.html<br />http://www.mkyong.com/spring-mvc/spring-3-mvc-contentnegotiatingviewresolver-example/<br />http://www.ibm.com/developerworks/web/library/wa-spring3webserv/index.html<br />http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/<br />
  7. 7. 개념 살펴보기<br />
  8. 8. @Controller<br />@Controller<br />@RequestMapping("/restful")<br />public class RestfulController {<br />@RequestMapping(value = "/message/{name}", method = RequestMethod.GET)<br /> public String getMessage(@PathVariable String name, ModelMap model) {<br />model.addAttribute("message", name);<br /> return "list";<br /> }<br />…<br />}<br />
  9. 9. Request Parameter Type<br />Strong<br />@Controller<br />@RequestMapping("/restful")<br />public class RestfulController {<br /> @RequestMapping(value = "/message/{name}", method = RequestMethod.GET)<br /> public String getMessage(@PathVariableString name, ModelMap model) {<br />model.addAttribute("message", name);<br /> return "list";<br /> }<br />…<br />}<br />
  10. 10. @PathVariable<br />@Controller<br />@RequestMapping("/restful")<br />public class RestfulController {<br /> @RequestMapping(value = "/message/{name}", method = RequestMethod.GET)<br /> public String getMessage(@PathVariable String name, ModelMap model) {<br />model.addAttribute("message", name);<br /> return "list";<br /> }<br />…<br />}<br />
  11. 11. @ResponseBody<br />@Controller<br />@RequestMapping("/restful")<br />public class RestfulController {<br />@RequestMapping(value = "/messagebody/{message}", method = RequestMethod.GET)<br />@ResponseBody<br />public Body getMessageBody(@PathVariable String message, ModelMap model) {<br />Body body = new Body();<br />body.setMessage(message);<br />return body;<br />}<br />…<br />}<br />@XmlRootElement(name = "body")<br />public class Body {<br />@XmlElement<br />private String msg;<br />public String getMessage() {<br /> return msg;<br />}<br />public void setMessage(String message) {<br /> this.msg = message;<br />}<br />}<br />
  12. 12. @ResponseStatus<br />@RequestMapping(value = "/exception", method = RequestMethod.GET)<br />public String throwException() {<br /> throw new ResourceNotFoundException();<br />}<br />@ExceptionHandler(ResourceNotFoundException.class)<br />@ResponseStatus(value = HttpStatus.BAD_GATEWAY) <br />public void handleNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {<br />System.out.println("handleNotFoundException:" + ex);<br />}<br />…..<br />class ResourceNotFoundException extends RuntimeException { <br />} <br />$ curl -i localhost:8080/restful/exception<br />HTTP/1.1 502 Bad Gateway<br />Content-Length: 0<br />Server: Jetty(6.1.26)<br />결과<br />
  13. 13. Rest 방식과 기존 파라미터요청 방식 을 같이 사용가능??<br />@RequestMapping(value = "/test/{name}/id/{id}", method = RequestMethod.GET)<br />public String getString(@PathVariable String name, @PathVariableint id, String email, ModelMap model) {<br />model.addAttribute("message", name);<br />model.addAttribute("id", id);<br />model.addAttribute("email", email);<br /> return "test";<br />}<br />가능<br />// test.jsp<br /><html><br /><head></head><br /><body><br /><h1>Test</h1><br /><h3>message : ${message}</h3><br /><h3>email : ${email}</h3><br /></body><br /></html><br />$ curl http://localhost:8080/restful/test/jaja/id/11?email=aaa@google.com<br /><html><br /><head></head><br /><body><br /> <h1>Test</h1><br /> <h3>message : jaja</h3><br /> <h3>email : aaa@google.com</h3><br /></body><br /></html><br />결과<br />
  14. 14. JSON Payload 요청 #1<br />@Controller<br />@RequestMapping("/comm")<br />@ResponseStatus(value = HttpStatus.ACCEPTED)<br />public class JsonRequestController {<br /> @RequestMapping(method = RequestMethod.GET) <br /> @ResponseStatus(value=HttpStatus.FOUND)<br /> public void sendFruitMessage(@RequestBody Message name) {<br />System.out.println("name : " + name.getName());<br /> return ;<br /> }<br />}<br />Spring MVC가 <br />알아서 deserialization을 해줌<br />@XmlRootElement(name = "message")<br />public class Message {<br />@XmlElement<br />private String name;<br />public String getName() {<br />return name;<br />}<br /> public void setName(String name) {<br /> this.name = name;<br /> }<br />}<br />
  15. 15. JSON Payload 요청 #2<br />결과 : xml <br /><클라이언트><br />$ curl -i -H "Content-Type: application/xml" -X get -d '<message><name>Kim Yong Hwan</name></message>' localhost:8080/comm<br />HTTP/1.1 302 Found<br />Content-Length: 0<br />Server: Jetty(6.1.26)<br /><서버><br />name : Kim Yong Hwan<br />결과 : json<br /><클라이언트><br />$ curl -i -H "Content-Type: application/json" -X get -d '{"name":"Kim Yong Hwan"}' localhost:8080/comm<br />HTTP/1.1 302 Found<br />Content-Length: 0<br />Server: Jetty(6.1.26)<br /><서버><br />name : Kim Yong Hwan<br />
  16. 16. DEMO<br />
  17. 17. WEB.XML<br /><web-app id="WebApp_ID" version="2.4"<br />xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><br /><display-name>Spring Web MVC Rest Demo Application</display-name><br /><servlet><br /> <servlet-name>mvc-dispatcher</servlet-name><br /> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><br /> <load-on-startup>1</load-on-startup><br /></servlet><br /><servlet-mapping><br /> <servlet-name>mvc-dispatcher</servlet-name><br /> <url-pattern>/</url-pattern><br /></servlet-mapping><br /><context-param><br /> <param-name>contextConfigLocation</param-name><br /> <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value><br /></context-param><br /><listener><br /> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class><br /></listener><br /></web-app><br />
  18. 18. mvc-dispatcher-servlet.xml<br /><beans xmlns="http://www.springframework.org/schema/beans"<br />xmlns:context="http://www.springframework.org/schema/context"<br />xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />xsi:schemaLocation="<br /> http://www.springframework.org/schema/beans <br /> http://www.springframework.org/schema/beans/spring-beans-3.0.xsd<br /> http://www.springframework.org/schema/context <br /> http://www.springframework.org/schema/context/spring-context-3.0.xsd<br /> http://www.springframework.org/schema/mvc<br /> http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"><br /><context:component-scan base-package="com.google.controller" /><br /><mvc:annotation-driven /><br /><bean<br />class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br /> <property name="prefix" value="/WEB-INF/pages/" /><br /> <property name="suffix" value=".jsp" /><br /></bean><br /></beans><br />
  19. 19. RestfulController.java<br />@Controller<br />@RequestMapping("/restful")<br />public class RestfulController {<br />@RequestMapping(value = "/message/{name}", method = RequestMethod.GET)<br /> public String getMessage(@PathVariable String name, ModelMap model) {<br />model.addAttribute("message", name);<br /> return "list";<br /> }<br />@RequestMapping(value = "/command/{id}/content/{content}", method = RequestMethod.GET)<br /> public String getCommand(@PathVariable("id") String id, @PathVariable("content") long content, ModelMap model) {<br />model.addAttribute("id", id);<br />model.addAttribute("content", content);<br /> return "command";<br /> }<br />@RequestMapping(value = "/link/{id}", method = RequestMethod.DELETE)<br /> public String deleteLink(@PathVariable("id") String id, ModelMap model) {<br />model.addAttribute("id", id);<br /> return "delete";<br /> }<br />}<br />
  20. 20. JSP 소스<br />// WEB-INF/pages/list.jsp<br /><html><br /><body><br /><h1>Spring MVC Restful Test</h1><br /><h3>message : ${message}</h3><br /></body><br /></html><br />// WEB-INF/pages/command.jsp<br /><html><br /><body><br /><h1>Spring MVC Restful Test</h1><br /> <h3>command id : ${id}</h3><br /><h3>content : ${content}</h3><br /></body><br /></html><br />// WEB-INF/pages/delete.jsp<br /><html><br /><body><br /><h1>Spring MVC Restful Test</h1><br /><h3>deleted id : ${id}</h3><br /></body><br /></html><br />
  21. 21. 결과 (웹 브라우저)<br />http://localhost:8080/restful/message/reset<br />http://localhost:8080/restful/command/aa/content/111<br />
  22. 22. 결과 (리눅스/Cygwin)<br />curl -X DELETE http://localhost:8080/restful/link/1<br /> curl -X DELETE http://localhost:8080/restful/message/3<br />
  23. 23. Content Negotiation<br />
  24. 24. Content Negotiation<br />HTTP 1.1 스펙에서 정의<br />의미<br />media type, 언어, 문자집합, 인코딩 등에 대해 브라우저가 제공한 선호도에 따라 자원의 가장 적합한 표현을 선택. <br />불완전한 협상 정보를 보내는 브라우저의 요청을 지능적으로 처리하는 기능<br />일반적으로 다른 프로토콜을 쓰려면, request의 “type” 파라미터를 확인하고, 이에 맞는 marshalling정보를 전달해야 한다.<br />트위터API가 이렇게 사용되고 있음<br />Spring MVC에서는 한번에 해결해주는 클래스 사용 ContentNegotiatingViewResolver<br />
  25. 25. DEMO<br />
  26. 26. FruitController<br />@Controller<br />@RequestMapping("/fruit")<br />public class FruitController{<br /> @RequestMapping(value="{fruitName}", method = RequestMethod.GET)<br /> public String getFruit(@PathVariable String fruitName, ModelMap model) {<br /> Fruit fruit = new Fruit(fruitName, 1000);<br />model.addAttribute("model", fruit);<br /> return "listfruit";<br /> }<br />}<br />
  27. 27. Fruit<br />@XmlRootElement(name = "fruit")<br />public class Fruit {<br />String name;<br />intquality;<br />public Fruit() {}<br />public Fruit(String name, int quality) {<br /> this.name = name;<br />this.quality = quality;<br />}<br />@XmlElement<br />public void setName(String name) {<br />this.name = name;<br />}<br />@XmlElement<br />public void setQuality(int quality) {<br />this.quality = quality;<br />}<br />}<br />
  28. 28. mvc-dispatcher-servlet.xml<br /><bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><br /> <property name="order" value="1" /><br /> <property name="mediaTypes"><br /><map><br /> <entry key="json" value="application/json" /><br /> <entry key="xml" value="application/xml" /><br /> <entry key="rss" value="application/rss+xml" /><br /></map><br /> </property><br /> <property name="defaultViews"><br /><list><br /> <!-- JSON View --><br /> <bean<br />class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"><br /> </bean><br /> <!-- RSS View --><br /> <bean class="com.google.rss.RssFeedView" /><br /> <!-- JAXB XML View --><br /> <bean class="org.springframework.web.servlet.view.xml.MarshallingView"><br /><constructor-arg><br /><bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller"><br /> <property name="classesToBeBound"><br /><list><br /> <value>com.google.bean.Fruit</value><br /></list><br /> </property><br /></bean><br /></constructor-arg><br /> </bean><br /> </list><br /> </property><br /> <property name="ignoreAcceptHeader" value="true" /><br /></bean><br /><bean<br />class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br /><property name="order" value="2" /><br /><property name="prefix" value="/WEB-INF/pages/" /><br /><property name="suffix" value=".jsp" /><br /></bean><br />
  29. 29. 결과<br />$ curl -H 'Accept: application/xml' localhost:8080/fruit/banana.xml<br /><?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruit><name>banana</name<br />><quality>1000</quality></fruit><br />$ curl -H 'Accept: application/rss'localhost:8080/fruit/banana.rss<br /><?xml version="1.0" encoding="UTF-8"?><br /><rssxmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><br /> <channel><br /> <title>Sample Title</title><br /> <link>http://google.com</link><br /> <description>Sample Description</description><br /> <item><br /> <link>http://www.google.com</link><br /> <content:encoded>banana1000</content:encoded><br /> <author>Test</author><br /> </item><br /> </channel><br /></rss><br />$ curl -H 'Accept: application/json' localhost:8080/fruit/banana.json<br />{"model":{"quality":1000,"name":"banana"}}<br />
  30. 30. Demo에서 @XmlElement사용시 유의할 점<br />Set메소드를 사용하려면, 다음과 같이 한다. <br />클래스의 멤버 필드에서 @XmlElement를 정의할 때는 set 메소드를 사용하지 않는다. <br />@XmlRootElement(name = "fruit")<br />public class Fruit {<br /> private String name;<br /> private int quality;<br />@XmlElement<br /> public void setBody(Body body) {<br />this.body= body;<br /> }<br /> public String getName() {<br /> return name;<br /> }<br />@XmlElement<br /> public void setQuality(int quality) {<br />this.quality= quality;<br /> }<br /> public intgetQuality() {<br /> return quality;<br /> }<br />…<br />}<br />@XmlRootElement(name = "fruit")<br />public class Fruit {<br />@XmlElement<br /> private String name;<br />@XmlElement<br /> private int quality;<br /> public String getName() {<br /> return name;<br /> }<br /> public intgetQuality() {<br /> return quality;<br /> }<br />…<br />}<br />JAXB에서 property를 읽을 때, 잘 사용해야 하는 구조<br />
  31. 31. Demo에서 @XmlElement사용시유의할 점<br />Body 클래스앞에@XmlRootElement선언이 되어 있으면, Fruit 클래스 에서 @XmlElement를 사용하지 않아도 된다.<br />@XmlRootElement(name = "body")<br />public class Body {<br /> @XmlElement<br /> private String msg;<br /> // set/get accessory ….<br />}<br />@XmlRootElement(name = "fruit")<br />public class Fruit {<br />@XmlElement<br /> private String name;<br />@XmlElement<br /> private int quality;<br /> private Body body;<br /> public String getName() {<br /> return name;<br /> }<br /> public intgetQuality() {<br /> return quality;<br /> }<br />…<br />}<br />
  32. 32. 웹 브라우져에서의PUT/DELETE의 제약<br />
  33. 33. 웹 브라우져 제약 #1<br />GET/POST만 쓸 수 있는 웹 브라우져가 있을 수 있다. PUT과 DELETE 을 쓰기 위해서는 trick을 써야 한다.<br />HiddenHttpMethodFilter이용<br />Web.xml<br />  <filter>        <filter-name>httpMethodFilter</filter-name>        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>    </filter>        <filter-mapping>        <filter-name>httpMethodFilter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping><br />
  34. 34. 웹 브라우져 제약 #2<br />// spring mvc form tag<br /><form:form method="delete"> <br /><p class="submit"><input type="submit" value="Delete Pet"/></p> <br /></form:form><br />@RequestMapping(method = RequestMethod.DELETE)<br />public String deletePet(@PathVariableintownerId, @PathVariableintpetId) {<br />this.clinic.deletePet(petId);<br /> return "redirect:/owners/" + ownerId;<br />}<br />아마도 예전처럼 내부적으로 name=“_method” value=“Delete” 하는 형태로 <br />전달하고, Spring 서버는 이 정보를 바탕으로 구현 했을 것으로 예상<br /><form action="POST"><br />  <input type="hidden" id="_method" value="PUT"><br /></form><br />
  35. 35. PUT/DELETE in HTML<br />HTML version 4 과 XHTML 1에서는 HTML form안의 HTTP 요청은 GET과 POST 방식만 허용. 그동안put/delete 메소드를 사용하려면, XMLHttpRequest를 이용하였음<br />HTTP상에서는 ok!<br />HTML5에서 put/delete는 사용하지 못한다고 나와 있음<br />http://www.w3.org/TR/html5-diff/<br />Using PUT and DELETE as HTTP methods for the form element is no longer supported.<br />참고<br />
  36. 36. 클라이언트 API : RestTemplate<br />
  37. 37. 클라이언트 API<br />Apache Commons의 HttpClient대신 쉽게 사용할 수 있는 RestTemplate구성<br /><bean id="restTemplate" <br />class="org.springframework.web.client.RestTemplate"><br /><property name="messageConverters"><br /> <list><br /> <ref bean="marshallingConverter" /><br /> <ref bean="atomConverter" /><br /> <ref bean="jsonConverter" /><br /> </list><br /></property><br /></bean><br />
  38. 38. 클라이언트 API<br />XML 요청<br />HttpHeaders headers = new HttpHeaders();<br />headers.setContentType(MediaType.APPLICATION_XML);<br />HttpEntity<String> entity = new HttpEntity<String>(headers);<br />ResponseEntity<EmployeeList> response = restTemplate.exchange(<br />"http://localhost:8080/rest/service/emps", <br />HttpMethod.GET, entity, EmployeeList.class);<br />EmployeeListingemployees = response.getBody();<br />// handle the employees<br />
  39. 39. 클라이언트 API<br />새직원포스팅/ 삭제<br />Employee newEmp = new Employee(11, “tguest", “tguest@google.com");<br />HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);<br />restTemplate.put(<br /> "http://localhost:8080/rest/service/emp/{id}", entity, "99");<br />restTemplate.delete(<br /> "http://localhost:8080/rest/service/emp/{id}", "99");<br />
  40. 40. RestTemplate클래스<br />클라이언트에서 사용할 수 있는 Rest API<br />
  41. 41. RestTemplate클래스<br />
  42. 42. RestTemplate예제<br />Map<String, String> vars = new HashMap<String, String>();<br />vars.put("id", "111");<br />vars.put("content", "222");<br />RestTemplaterestTemplate = new RestTemplate();<br />String result = restTemplate.getForObject("http://localhost:8080/restful/command/{id}/content/{content}", String.class, vars);<br />System.out.println("result : " + result);<br />result : <html><br /><body><br /><h1>Spring MVC Restful Test</h1><br /> <h3>command id : 111</h3><br /><h3>content : 222</h3><br /></body><br /></html><br />결과<br />
  43. 43. ExampleCode<br />Knight76.tistory.com에서 ‘spring mvc restful’ 검색<br />
  44. 44. End of Document<br />

×