Wiki source code of ERRest In Depth

Last modified by Pascal Robert on 2012/06/10 16:01

Hide last authors
Pascal Robert 45.1 1 General architecture
Pascal Robert 46.1 2 Same Origin policy
3 Transactions
4 HTML vs other formats
5 Response representation
6 Missing route
7 Missing object
8 POJO objects
9 Headers
10 Caching
11 Adding new format
12 Security
13 strictMode
14 Workflow
15 Query arguments and RXRestFetchSpecification
16 ERXRestNameRegistry
17 MapClassDescription / NSDictionaryClassDescription
Pascal Robert 45.1 18
19 Calling an action goes like this:
20
21 {{code}}
22
23 Application.dispatchRequest ->
24 ERXRouteRequestHandler.handleRequest ->
25 ERXRouteRequestHandler._handleRequest ->
26 RestEntitiesController(ERXRouteController).performActionNamed(String actionName, false throwExceptions) ->
27 RestEntitiesController(ERXRouteController).performRouteActionNamed -> RestEntitiesController(ERXRouteController).performActionWithArguments(Method, Object...) ->
28 Method.invoke -> ...
29 RestEntitiesController.indexAction()
30
31 {{/code}}
32
33 performActionNamed:
34
35 {{code}}
36
37 if (transactionAdaptor.transactionsEnabled() && !transactionAdaptor.isExecutingTransaction(context(), request())) {
38 if (!transactionAdaptor.willHandleRequest(context(), request())) {
39 if (transactionAdaptor.didHandleRequest(context(), request())) {
40 results = stringResponse("Transaction request enqueued.");
41 } else {
42 results = stringResponse("Transaction executed.");
43 }
44 }
45 }
46
47 if (results == null) {
48 checkAccess();
49 }
50
51 if (results == null && isAutomaticHtmlRoutingEnabled() && format() == ERXRestFormat.html()) {
52 results = performHtmlActionNamed(actionName);
53 }
54
55 if (results == null) {
56 results = performRouteActionNamed(actionName);
57 }
58
59 if (results == null) {
60 results = response(null, ERXKeyFilter.filterWithAttributes());
61 }
62 else if (results instanceof IERXRouteComponent) {
63 _takeRouteParametersFromRequest(results);
64 }
65
66 {{/code}}
67
68 ERXRouteController.checkAccess()
69
70 Will do nothing by default. Override it to check for security.
71
72 {{code}}
73
74 protected void checkAccess() throws SecurityException {
75 }
76
77 {{/code}}
78
79 ERXRouteController.performHtmlActionNamed
80
81 {{code}}
82 protected WOActionResults performHtmlActionNamed(String actionName) throws Exception {
83 WOActionResults results = null;
84
85 String pageName = pageNameForAction(actionName);
86 if (_NSUtilities.classWithName(pageName) != null) {
87 try {
88 results = pageWithName(pageName);
89 if (!(results instanceof IERXRouteComponent)) {
90 log.error(pageName + " does not implement IERXRouteComponent, so it will be ignored.");
91 results = null;
92 }
93 }
94 catch (WOPageNotFoundException e) {
95 log.info(pageName + " does not exist, falling back to route controller.");
96 results = null;
97 }
98 }
99 else {
100 log.info(pageName + " does not exist, falling back to route controller.");
101 }
102
103 if (results == null && shouldFailOnMissingHtmlPage()) {
104 results = performUnknownAction(actionName);
105 }
106
107 return results;
108 }
Pascal Robert 46.1 109 {{/code}}
Pascal Robert 45.1 110
111 {{code}}
112 protected String pageNameForAction(String actionName) {
113 return entityName() + ERXStringUtilities.capitalize(actionName) + "Page";
114 }
Pascal Robert 46.1 115 {{/code}}
Pascal Robert 45.1 116
117 {{code}}
118
119 protected boolean shouldFailOnMissingHtmlPage() {
120 return false;
121 }
122
123 {{/code}}
124
125 ERXRestUtils
126
Pascal Robert 46.1 127 request -> route
Pascal Robert 45.1 128
129 {{code}}
130
131 /**
132 * A NameFormat that behaves like Rails -- plural entities, plural routes, lowercase underscore names
133 * (names_like_this).
134 */
135 public static NameFormat RAILS = new NameFormat(true, true, NameFormat.Case.LowercaseUnderscore);
136
137 /**
138 * A NameFormat that behaves like WO -- singular entities, singular routes, camel names (NamesLikeThis).
139 */
140 public static NameFormat WO = new NameFormat(false, false, NameFormat.Case.CamelCase);
141
142 /**
143 * A NameFormat that behaves like WO -- singular entities, singular routes, lowercase camel names (namesLikeThis).
144 */
145 public static NameFormat WO_LOWER = new NameFormat(false, false, NameFormat.Case.LowerCamelCase);
146
147 {{/code}}
148
Pascal Robert 37.1 149 * ERXRestContext
150 ** contains the editing context and an userInfo dictionnary
151 ** will be populated with er.rest.dateFormat, er.rest.timestampFormatter and er.rest.timestampFormat (read only for non-HTML responses)
Pascal Robert 46.1 152 *** want to change the time format for a specific controller?
153 ** {{code}}
Pascal Robert 37.1 154 protected ERXRestContext createRestContext() {
155 ERXRestContext restContext = new ERXRestContext(editingContext());
156 restContext.setUserInfoForKey("yyyy-MM-dd", "er.rest.dateFormat");
157 restContext.setUserInfoForKey("yyyy-MM-dd", "er.rest.timestampFormat");
158 return restContext;
159 }
Pascal Robert 46.1 160 {{/code}}you just need to override createRestContext() in your controller if you want to add other stuff to the context (a user, etc.)
Pascal Robert 37.1 161 * Properties
162 ** ERXRest.idKey (ERXRestFormatDelegate)
Pascal Robert 46.1 163 *** (default "id") Override this property if you want to use a different key for the 'id' attribute~*~* ERXRest.typeKey
Pascal Robert 37.1 164 ** ERXRest.nilKey (ERXRestFormatDelegate)
165 ** ERXRest.writeNilKey (ERXRestFormatDelegate)
166 ** ERXRest.pluralEntityNames (ERXRestFormatDelegate)
167 ** ERXRest.writeTypeKey (ERXRestFormatDelegate)
Pascal Robert 39.1 168 ** ERXRest.suppressTypeAttributesForSimpleTypes (ERXXmlRestWriter)
169 *** (default "false") If set to true, primitive types, like type = "datetime", won't be added to the output
Pascal Robert 37.1 170 ** ERXRest.strictMode
Pascal Robert 39.1 171 *** In ERXMissingRouteController: (default "true") If set to true, status code in the response will be 405 Not Allowed, if set to false, status code will be 404 Not Found
172 *** In ERXRouteController: (default "true") If set to true, status code in the response will be 405 Not Allowed, if set to false, status code will be 404 Not Found
173 *** ERXRouteResults: (default "true") If set to true, creating a ressource will return status code 201 Created, if set to false, will return 200 OK
Pascal Robert 37.1 174 ** ERXRest.pluralEntityNames (ERXRouteRequestHandler)
175 ** ERXRest.routeCase (ERXRouteRequestHandler)
176 ** ERXRest.lowercaseEntityNames (ERXRouteRequestHandler)
177 ** ERXRest.parseUnknownExtensions (ERXRouteRequestHandler)
178 *** (default "true") If set to "false", will return a 404 status code if the format doesn't exist
179 ** ERXRest.missingControllerName (ERXRouteRequestHandler)
180 *** (default "ERXMissingRouteController") Allow you to specify which controller to use when a route doesn't exist
181 ** er.rest.rfcDateFormat
182 ** er.rest.dateFormat
183 ** er.rest.dateFormat.primary
184 ** er.rest.dateFormat.secondary
185 ** er.rest.dateFormatter
186 ** er.rest.timestampFormat
187 ** er.rest.timestampFormat.primary
188 ** er.rest.timestampFormat.secondary
189 ** er.rest.timestampFormatter
190 ** er.rest.rfcDateFormat
Pascal Robert 45.1 191 ** er.rest.jodaTime
192 ** ERXRest.transactionsEnabled (default 'false') ERXRestTransactionRequestAdaptor
193 ** ERXRest.maxEventsPerTransaction (default '50') ERXRestTransactionRequestAdaptor
194 ** ERXRest.accessControlAllowRequestHeaders (ERXRouteController)
195 ** ERXRest.accessControlAllowRequestMethods (ERXRouteController)
196 ** ERXRest.defaultFormat (ERXRouteController)
197 *** (default "xml") Allow you to set the default format for all of your REST controllers
198 ** ERXRest.allowWindowNameCrossDomainTransport (ERXRouteController)
199 ** ERXRest.accessControlMaxAge (ERXRouteController)
Pascal Robert 46.1 200 *** (default 1728000) This header indicates how long the results of a preflight request can be cached. For an example of a preflight request, see the above examples.
Pascal Robert 45.1 201 ** ERXRest.accessControlAllowOrigin (ERXRouteController)
Pascal Robert 46.1 202 *** Set the value to '*' to enable all origins. See [[https:~~/~~/developer.mozilla.org/En/HTTP_access_control#Access-Control-Allow-Origin>>url:https://developer.mozilla.org/En/HTTP_access_control#Access-Control-Allow-Origin||shape="rect"]]
Pascal Robert 37.1 203 * JSON Schema
Pascal Robert 46.1 204 **
205
206 {{code}}
207
Pascal Robert 37.1 208 /something?schema=true
209
210 public WOActionResults indexAction() {
211 if (isSchemaRequest()) {
212 return schemaResponse(showFilter());
213 }
214 protected boolean isSchemaRequest() {
215 return request().stringFormValueForKey("schema") != null;
216 }
Pascal Robert 46.1 217
Pascal Robert 37.1 218 {{/code}}
219
Pascal Robert 4.1 220 Application(ERXApplication).dispatchRequest(WORequest) line: 2051
Pascal Robert 46.1 221 ERXRouteRequestHandler(WOActionRequestHandler)._handleRequest(WORequest) line: 221
222 ERXRouteRequestHandler.getRequestHandlerPathForRequest(WORequest) line: 782
Pascal Robert 4.1 223
Pascal Robert 46.1 224 ERXRouteRequestHandler(WOActionRequestHandler)._handleRequest(WORequest) line: 259
225 PagesController(ERXRouteController).performActionNamed(String) line: 1328
226 PagesController(ERXRouteController).performActionNamed(String, boolean) line: 1385
227 PagesController(ERXRouteController).performRouteActionNamed(String) line: 1510
228 PagesController(ERXRouteController).performActionWithArguments(Method, Object...) line: 1559
229 ...
230 PagesController.mainPageAction() line: 20
Pascal Robert 27.1 231
232 == ERXRouteRequestHandler ==
233
Pascal Robert 46.1 234 |(((
235 **Properties**
236 )))
237 |(((
238 ERXRest.missingControllerName
239 )))|(((
240 (default "ERXMissingRouteController")
241
242 )))
243 |(((
244 ERXRest.parseUnknownExtensions
245 )))|(((
246 ERXRest.parseUnknownExtensions
247
248 )))
249 |(((
250 ERXRest.pluralEntityNames
251 )))|(((
252 ERXRest.pluralEntityNames
253
254 )))
255 |(((
256 ERXRest.routeCase
257 )))|(((
258 ERXRest.routeCase
259
260 )))
261 |(((
262 ERXRest.lowercaseEntityNames
263 )))|(((
264 ERXRest.lowercaseEntityNames
265 )))
Pascal Robert 27.1 266
267 ERXMissingRouteController is the controller that is used when no route can be found. It's "missing" action is loaded.
268
Pascal Robert 46.1 269 |(((
270 **Properties**
271 )))
272 |(((
273 ERXRest.strictMode
274 )))|(((
275 ERXRest.strictMode
276
277 )))
278
Pascal Robert 27.1 279 ERXRouteController