Wiki source code of ERRest In Depth

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

Show last authors
1 General architecture
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
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 }
109 {{/code}}
110
111 {{code}}
112 protected String pageNameForAction(String actionName) {
113 return entityName() + ERXStringUtilities.capitalize(actionName) + "Page";
114 }
115 {{/code}}
116
117 {{code}}
118
119 protected boolean shouldFailOnMissingHtmlPage() {
120 return false;
121 }
122
123 {{/code}}
124
125 ERXRestUtils
126
127 request -> route
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
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)
152 *** want to change the time format for a specific controller?
153 ** {{code}}
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 }
160 {{/code}}you just need to override createRestContext() in your controller if you want to add other stuff to the context (a user, etc.)
161 * Properties
162 ** ERXRest.idKey (ERXRestFormatDelegate)
163 *** (default "id") Override this property if you want to use a different key for the 'id' attribute~*~* ERXRest.typeKey
164 ** ERXRest.nilKey (ERXRestFormatDelegate)
165 ** ERXRest.writeNilKey (ERXRestFormatDelegate)
166 ** ERXRest.pluralEntityNames (ERXRestFormatDelegate)
167 ** ERXRest.writeTypeKey (ERXRestFormatDelegate)
168 ** ERXRest.suppressTypeAttributesForSimpleTypes (ERXXmlRestWriter)
169 *** (default "false") If set to true, primitive types, like type = "datetime", won't be added to the output
170 ** ERXRest.strictMode
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
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
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)
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.
201 ** ERXRest.accessControlAllowOrigin (ERXRouteController)
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"]]
203 * JSON Schema
204 **
205
206 {{code}}
207
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 }
217
218 {{/code}}
219
220 Application(ERXApplication).dispatchRequest(WORequest) line: 2051
221 ERXRouteRequestHandler(WOActionRequestHandler)._handleRequest(WORequest) line: 221
222 ERXRouteRequestHandler.getRequestHandlerPathForRequest(WORequest) line: 782
223
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
231
232 == ERXRouteRequestHandler ==
233
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 )))
266
267 ERXMissingRouteController is the controller that is used when no route can be found. It's "missing" action is loaded.
268
269 |(((
270 **Properties**
271 )))
272 |(((
273 ERXRest.strictMode
274 )))|(((
275 ERXRest.strictMode
276
277 )))
278
279 ERXRouteController