Programming Guide - Web Services (RESTful) » History » Version 2
chin-yeh, 12/27/2011 12:00 PM
1 | 1 | chin-yeh | {{toc}} |
---|---|---|---|
2 | |||
3 | h1. Programming Guide - Web Services (RESTful) |
||
4 | |||
5 | This guide describes how to develop the _RESTful_ based web services. |
||
6 | |||
7 | In general, the web services should contains the following layers: |
||
8 | * *Web Services Layer* _(a.k.a controller)_ - this is the only place where the client will talk to |
||
9 | * *Services Layer* - contains the business logic on how to handle the client's request |
||
10 | * *DAO layer* - contains the logic on how to retrieve & persist data |
||
11 | |||
12 | !web_services_layers.png! |
||
13 | |||
14 | h2. Step-By-Step |
||
15 | |||
16 | > Assume that you have setup the [[General Info:Setup Development Environment|Eclipse environment]] |
||
17 | |||
18 | # launch *Eclipse* |
||
19 | # create a new *Dynamic Web Project*: |
||
20 | ** _Dynamic web module version_ should set to _2.4_ or above |
||
21 | ** _JDK_ compiler compliance version should set to _5.0_ or above |
||
22 | # and then *configure* the project: |
||
23 | ## obtain the [[Programming Guide - Web Services (RESTful)#Project-Dependencies|project dependencies]] and then place it in the <code>WEB-INF/lib</code> folder |
||
24 | ## replace the *web.xml* with this attachment:"web.xml" |
||
25 | ## create the following folder: |
||
26 | *** <code>src/META-INF/spring</code> |
||
27 | ## copy attachment:"applicationContext.xml" to <code>src/META-INF/spring</code> folder |
||
28 | *** edit the following line to put in the *root package* of the project |
||
29 | <pre> |
||
30 | <code class="XML"> |
||
31 | <context:component-scan base-package="my.root.package"/> |
||
32 | </code> |
||
33 | </pre> |
||
34 | ## copy attachment:"restful-servlet.xml" to <code>src/META-INF/spring</code> folder |
||
35 | *** edit the following line to put in the *package name* which contains all of the *controller* classes |
||
36 | <pre> |
||
37 | <code class="XML"> |
||
38 | <context:component-scan base-package="my.package.controller"/> |
||
39 | </code> |
||
40 | </pre> |
||
41 | # after configured the project, let's start coding the following layers: |
||
42 | ** *Web Services/Controller* - _see_ [[Programming_Guide_-_Web_Services_(RESTful)#Controller-Layer|Coding Guidelines]] |
||
43 | ** *Services* - _see_ [[Programming_Guide_-_Web_Services_(RESTful)#Services-Layer|Coding Guidelines]] |
||
44 | ** *DAO* - _optional but it's highly recommended to implement it_ |
||
45 | # done |
||
46 | |||
47 | > download this sample project, attachment:sample_restful_project.zip to view all code snippets |
||
48 | |||
49 | h2. Project Dependencies |
||
50 | |||
51 | * com.springsource.org.aopalliance-1.0.0.jar |
||
52 | * jackson-core-asl-1.9.0.jar |
||
53 | * jackson-jaxrs-1.9.0.jar |
||
54 | * jackson-mapper-asl-1.9.0.jar |
||
55 | * jcl-over-slf4j-1.6.1.jar |
||
56 | * org.springframework.aop-3.0.5.RELEASE.jar |
||
57 | * org.springframework.asm-3.0.5.RELEASE.jar |
||
58 | * org.springframework.beans-3.0.5.RELEASE.jar |
||
59 | * org.springframework.context.support-3.0.5.RELEASE.jar |
||
60 | * org.springframework.context-3.0.5.RELEASE.jar |
||
61 | * org.springframework.core-3.0.5.RELEASE.jar |
||
62 | * org.springframework.expression-3.0.5.RELEASE.jar |
||
63 | * org.springframework.jdbc-3.0.5.RELEASE.jar |
||
64 | * org.springframework.transaction-3.0.5.RELEASE.jar |
||
65 | * org.springframework.web.servlet-3.0.5.RELEASE.jar |
||
66 | * org.springframework.web-3.0.5.RELEASE.jar |
||
67 | * slf4j-api-1.6.1.jar |
||
68 | * slf4j-log4j12-1.6.1.jar |
||
69 | * spring-security-config-3.0.5.RELEASE.jar |
||
70 | * spring-security-core-3.0.5.RELEASE.jar |
||
71 | * spring-security-web-3.0.5.RELEASE.jar |
||
72 | |||
73 | These dependencies can be found in: |
||
74 | * [[General Info:SearchBrowse_artifact|Nexus]] |
||
75 | * download attachment:web_inf_lib.zip and extract it to <code>WEB-INF/lib</code> folder |
||
76 | |||
77 | h1. Coding Guidelines |
||
78 | |||
79 | This guidelines will be revised anytime to accommodate requirement changes. |
||
80 | |||
81 | h2. Controller Layer |
||
82 | |||
83 | * the controller class must be *thread safe* as there's only *one instance* will be created to serve all web request |
||
84 | |||
85 | * every controller *must annotated* with <code>@Controller</code> at the class level |
||
86 | <pre> |
||
87 | <code class="JAVA"> |
||
88 | @Controller |
||
89 | public class RegistrationController { |
||
90 | ... |
||
91 | } |
||
92 | </code> |
||
93 | </pre> |
||
94 | |||
95 | * use the <code>@RequestMapping</code> to map the web request to the *handler class* or *handler method*, e.g.: |
||
96 | <pre> |
||
97 | <code class="JAVA"> |
||
98 | @Controller |
||
99 | @RequestMapping(value = "/reg-services") |
||
100 | public class RegistrationController { |
||
101 | |||
102 | @RequestMapping(value = "/shopper", method = RequestMethod.POST) |
||
103 | @ResponseBody |
||
104 | public boolean registerMember(@RequestBody MemberRegistrationRequest request) { |
||
105 | return registrationServices.registerMember(request); |
||
106 | } |
||
107 | } |
||
108 | </code> |
||
109 | </pre> |
||
110 | ** for this example, the following web request will be mapped to the *registerMember* method: |
||
111 | *** *request URL* - http://.../.../reg-services/shopper |
||
112 | *** *request method* - POST |
||
113 | |||
114 | * refer to this "link":http://en.wikipedia.org/wiki/Representational_state_transfer#RESTful_web_services for the guidelines about how to choose the appropriate *HTTP methods* and *request URI* |
||
115 | |||
116 | * every *method handler* should be annotated with <code>@ResposeBody</code> at the method level and return a value _(do not return <code>void</code>)_ |
||
117 | <pre> |
||
118 | <code class="JAVA"> |
||
119 | @ResponseBody |
||
120 | public boolean registerMember(@RequestBody MemberRegistrationRequest request) { |
||
121 | return registrationServices.registerMember(request); |
||
122 | } |
||
123 | </code> |
||
124 | </pre> |
||
125 | |||
126 | * if an argument is annotated with <code>@RequestBody</code>, the body of the web request will be automatically parsed. |
||
127 | <pre> |
||
128 | <code class="JAVA"> |
||
129 | public boolean registerMember(@RequestBody MemberRegistrationRequest request) { |
||
130 | </code> |
||
131 | </pre> |
||
132 | |||
133 | 2 | chin-yeh | * to bound the value of a *URI template variable*, just annotate, <code>@PathVariable</code> to the *target argument* |
134 | 1 | chin-yeh | <pre> |
135 | <code class="JAVA"> |
||
136 | @RequestMapping(value = "/shopper/{shopperRefNo}", method = RequestMethod.GET) |
||
137 | @ResponseBody |
||
138 | public MemberInfo retrieveMemberInfo(@PathVariable int refNo) { |
||
139 | ... |
||
140 | </code> |
||
141 | </pre> |
||
142 | ** for this example, the value of the URI template variable, *shopperRefNo* will be bound to the argument, *refNo*. |
||
143 | |||
144 | * use <code>@RequestParam</code> to access the specific *Servlet request paramater*. Parameter values will be converted to the declared argument type. |
||
145 | <pre> |
||
146 | <code class="JAVA"> |
||
147 | public String setupForm(@RequestParam("petId") int petId) |
||
148 | </code> |
||
149 | </pre> |
||
150 | ** *note:* By default, the parameter is required. To make the parameter as optional, set the *required* attribute to *false*, e.g.: |
||
151 | *** <code>@RequestParam(value="id", required=false)</code> |
||
152 | |||
153 | h2. Services Layer |
||
154 | |||
155 | * the service class is just a *POJO* (plain-old-java-object) |
||
156 | |||
157 | * the service class must be *thread safe* if, and only if it is one of the *field members* of the *controller* |
||
158 | |||
159 | * the service class should contains the *business logics only*. Try to delegate the un-relevant codes to other appropriate layers. |