Wiki source code of ERRest In Depth

Version 45.1 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
83 protected WOActionResults performHtmlActionNamed(String actionName) throws Exception {
84 WOActionResults results = null;
85
86 String pageName = pageNameForAction(actionName);
87 if (_NSUtilities.classWithName(pageName) != null) {
88 try {
89 results = pageWithName(pageName);
90 if (!(results instanceof IERXRouteComponent)) {
91 log.error(pageName + " does not implement IERXRouteComponent, so it will be ignored.");
92 results = null;
93 }
94 }
95 catch (WOPageNotFoundException e) {
96 log.info(pageName + " does not exist, falling back to route controller.");
97 results = null;
98 }
99 }
100 else {
101 log.info(pageName + " does not exist, falling back to route controller.");
102 }
103
104 if (results == null && shouldFailOnMissingHtmlPage()) {
105 results = performUnknownAction(actionName);
106 }
107
108 return results;
109 }
110
111 {{/code}}
112
113 {{code}}
114
115 protected String pageNameForAction(String actionName) {
116 return entityName() + ERXStringUtilities.capitalize(actionName) + "Page";
117 }
118
119 {{/code}}
120
121 {{code}}
122
123 protected boolean shouldFailOnMissingHtmlPage() {
124 return false;
125 }
126
127 {{/code}}
128
129 ERXRestUtils
130
131 request > route
132
133 {{code}}
134
135 /**
136 * A NameFormat that behaves like Rails -- plural entities, plural routes, lowercase underscore names
137 * (names_like_this).
138 */
139 public static NameFormat RAILS = new NameFormat(true, true, NameFormat.Case.LowercaseUnderscore);
140
141 /**
142 * A NameFormat that behaves like WO -- singular entities, singular routes, camel names (NamesLikeThis).
143 */
144 public static NameFormat WO = new NameFormat(false, false, NameFormat.Case.CamelCase);
145
146 /**
147 * A NameFormat that behaves like WO -- singular entities, singular routes, lowercase camel names (namesLikeThis).
148 */
149 public static NameFormat WO_LOWER = new NameFormat(false, false, NameFormat.Case.LowerCamelCase);
150
151 {{/code}}
152
153 * ERXRestContext
154 ** contains the editing context and an userInfo dictionnary
155 ** will be populated with er.rest.dateFormat, er.rest.timestampFormatter and er.rest.timestampFormat (read only for non-HTML responses)
156 *** want to change the time format for a specific controller?
157
158 {{code}}
159
160 protected ERXRestContext createRestContext() {
161 ERXRestContext restContext = new ERXRestContext(editingContext());
162 restContext.setUserInfoForKey("yyyy-MM-dd", "er.rest.dateFormat");
163 restContext.setUserInfoForKey("yyyy-MM-dd", "er.rest.timestampFormat");
164 return restContext;
165 }
166
167 {{/code}}
168
169 *
170 ** you just need to override createRestContext() in your controller if you want to add other stuff to the context (a user, etc.)
171 * Properties
172 ** ERXRest.idKey (ERXRestFormatDelegate)
173 *** (default "id") Override this property if you want to use a different key for the 'id' attribute** ERXRest.typeKey**
174 ** ERXRest.nilKey (ERXRestFormatDelegate)
175 ** ERXRest.writeNilKey (ERXRestFormatDelegate)
176 ** ERXRest.pluralEntityNames (ERXRestFormatDelegate)
177 ** ERXRest.writeTypeKey (ERXRestFormatDelegate)
178 ** ERXRest.suppressTypeAttributesForSimpleTypes (ERXXmlRestWriter)
179 *** (default "false") If set to true, primitive types, like type = "datetime", won't be added to the output
180 ** ERXRest.strictMode
181 *** 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
182 *** 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
183 *** ERXRouteResults: (default "true") If set to true, creating a ressource will return status code 201 Created, if set to false, will return 200 OK
184 ** ERXRest.pluralEntityNames (ERXRouteRequestHandler)
185 ** ERXRest.routeCase (ERXRouteRequestHandler)
186 ** ERXRest.lowercaseEntityNames (ERXRouteRequestHandler)
187 ** ERXRest.parseUnknownExtensions (ERXRouteRequestHandler)
188 *** (default "true") If set to "false", will return a 404 status code if the format doesn't exist
189 ** ERXRest.missingControllerName (ERXRouteRequestHandler)
190 *** (default "ERXMissingRouteController") Allow you to specify which controller to use when a route doesn't exist
191 ** er.rest.rfcDateFormat
192 ** er.rest.dateFormat
193 ** er.rest.dateFormat.primary
194 ** er.rest.dateFormat.secondary
195 ** er.rest.dateFormatter
196 ** er.rest.timestampFormat
197 ** er.rest.timestampFormat.primary
198 ** er.rest.timestampFormat.secondary
199 ** er.rest.timestampFormatter
200 ** er.rest.rfcDateFormat
201 ** er.rest.jodaTime
202 ** ERXRest.transactionsEnabled (default 'false') ERXRestTransactionRequestAdaptor
203 ** ERXRest.maxEventsPerTransaction (default '50') ERXRestTransactionRequestAdaptor
204 ** ERXRest.accessControlAllowRequestHeaders (ERXRouteController)
205 ** ERXRest.accessControlAllowRequestMethods (ERXRouteController)
206 ** ERXRest.defaultFormat (ERXRouteController)
207 *** (default "xml") Allow you to set the default format for all of your REST controllers
208 ** ERXRest.allowWindowNameCrossDomainTransport (ERXRouteController)
209 ** ERXRest.accessControlMaxAge (ERXRouteController)
210 *** (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.
211 ** ERXRest.accessControlAllowOrigin (ERXRouteController)
212 *** Set the value to '**' to enable all origins. See [[https://developer.mozilla.org/En/HTTP_access_control#Access-Control-Allow-Origin]]**
213 * JSON Schema
214 ** {{code}}
215 /something?schema=true
216
217 public WOActionResults indexAction() {
218 if (isSchemaRequest()) {
219 return schemaResponse(showFilter());
220 }
221 protected boolean isSchemaRequest() {
222 return request().stringFormValueForKey("schema") != null;
223 }
224 {{/code}}
225
226 Application(ERXApplication).dispatchRequest(WORequest) line: 2051
227 ERXRouteRequestHandler(WOActionRequestHandler).//handleRequest(WORequest) line: 221
228 ERXRouteRequestHandler.getRequestHandlerPathForRequest(WORequest) line: 782//
229
230 ERXRouteRequestHandler(WOActionRequestHandler).//handleRequest(WORequest) line: 259
231 PagesController(ERXRouteController).performActionNamed(String) line: 1328
232 PagesController(ERXRouteController).performActionNamed(String, boolean) line: 1385
233 PagesController(ERXRouteController).performRouteActionNamed(String) line: 1510
234 PagesController(ERXRouteController).performActionWithArguments(Method, Object...) line: 1559
235 ...
236 PagesController.mainPageAction() line: 20//
237
238 == ERXRouteRequestHandler ==
239
240 | **Properties**
241 | ERXRest.missingControllerName | (default "ERXMissingRouteController")
242 \\          
243 | ERXRest.parseUnknownExtensions | ERXRest.parseUnknownExtensions
244 \\          
245 | ERXRest.pluralEntityNames | ERXRest.pluralEntityNames
246 \\          
247 | ERXRest.routeCase | ERXRest.routeCase
248 \\          
249 | ERXRest.lowercaseEntityNames | ERXRest.lowercaseEntityNames
250
251 ERXMissingRouteController is the controller that is used when no route can be found. It's "missing" action is loaded.
252
253 | **Properties**
254 | ERXRest.strictMode | ERXRest.strictMode
255 \\          |
256 ERXRouteController