Wiki source code of ERRest Framework
Version 57.1 by Pascal Robert on 2010/06/08 13:22
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | = Presentations = | ||
2 | |||
3 | A presentation was made by Mike on February 16th 2010 about ERREST. The presentation was recorded and made available here : | ||
4 | |||
5 | http:~/~/webobjects.mdimension.com/wonder/screencasts/ERRest-2010-02-16.mov | ||
6 | |||
7 | It's also listed on the Screencasts page on wocommunity.org and in the podcasts RSS feed. | ||
8 | |||
9 | Mike also did a session about it at WOWODC West 2009, you can buy this session in the Community Store. | ||
10 | |||
11 | = Class names vs entity names = | ||
12 | |||
13 | A question was asked about situations where your EO class name is different from your entity name. Mike's answer was : | ||
14 | |||
15 | Everything internally is based on entity names. Your class name has very little to do with things other than your own source code. So for example: | ||
16 | |||
17 | routeRequestHandler.addDefaultRoutes("SamsSchool"); ~/~/ School.ENTITY//NAME// | ||
18 | |||
19 | Regardless, you will always have: | ||
20 | public class SamsSchoolController extends ERXDefaultRouteController { | ||
21 | ... | ||
22 | School school = (School) routeObjectForKey("samsSchool"); | ||
23 | |||
24 | When those slides say "EntityName", they mean it :) | ||
25 | |||
26 | If you want to call it "School" to the outside, add this before you register the default routes for "SamsSchool": | ||
27 | |||
28 | ERXRestNameRegistry.registry().setExternalNameForInternalName("School", "SamsSchool"); ~/~/ "School", School.ENTITY//NAME// | ||
29 | |||
30 | After adding this, no other code changes. All you're saying is that the routes and type names that send over the wire should all say "School". | ||
31 | |||
32 | = WO HTTP adaptor = | ||
33 | |||
34 | The WO HTTP adaptor coming with WO 5.3 doesn't support PUT and DELETE operations, so you won't be able to use those two HTTP methods. You need to use either the 5.4 adaptor or the Wonder version of the adaptor. | ||
35 | |||
36 | = To-many relationships = | ||
37 | |||
38 | ERRest won't let you add or remove objects in a to-many relationship, it can only update an existing object that is in the relationship. To add a new object to a relationship, you first need to fetch (or create) the parent object and make a second call to create the object and add it to the relationship. | ||
39 | |||
40 | So let's say you have an existing Organization object and you want to add a Member to it. First, you need to fetch the Organization : | ||
41 | |||
42 | {{code}} | ||
43 | |||
44 | GET /cgi-bin/WebObjects/ra/Organization/1.json | ||
45 | |||
46 | {{/code}} | ||
47 | |||
48 | and after, you create a new Member : | ||
49 | |||
50 | {{code}} | ||
51 | |||
52 | POST /cgi-bin/WebObjects/ra/Organization/1/addMember.json | ||
53 | |||
54 | {{/code}} | ||
55 | |||
56 | = Same Origin policy = | ||
57 | |||
58 | If you are planning to offer your REST services to other people, they might run into Same Origin Policy problem. When using XMLHttpRequest on a page who is on a different domain than the REST service, XMLHttpRequest will tell you that it's not acceptable. | ||
59 | |||
60 | To get around that problem, many solutions has been found, but two of them are more accepted than the others : [[JSONP>>http://en.wikipedia.org/wiki/JSON#JSONP]] and [[HTTP//access//control>>https://developer.mozilla.org/en/HTTP_access_control]]. I didn't try the JSONP route, but I did try HTTP//access//control, and Dojo and Prototype are using this method to get around the Same Origin policy problem. | ||
61 | |||
62 | HTTP//access//control works by adding new headers in the request that says which HTTP methods the request wants to do, and it's send as a OPTIONS HTTP method. You MUST reply with some headers to this OPTIONS request, so you need to do two things : | ||
63 | |||
64 | {{warning}} | ||
65 | Look like the Same Origin Policy do not work in Safari 5, Safari doesn't send a OPTIONS call before the GET/POST/DELETE/PUT and it won't load up the JSON data in the JavaScript app. | ||
66 | {{/warning}} | ||
67 | |||
68 | 1) Create a method to handle the request in your route controller : | ||
69 | |||
70 | {{code}} | ||
71 | |||
72 | public WOActionResults optionsAction() throws Throwable { | ||
73 | ERXResponse response = new ERXResponse(); | ||
74 | response.setHeader("*", "Access-Control-Allow-Origin"); | ||
75 | response.setHeaders(this.request().headersForKey("Access-Control-Request-Method"), "Access-Control-Allow-Methods"); | ||
76 | response.setHeaders(this.request().headersForKey("Access-Control-Request-Headers"), "Access-Control-Allow-Headers"); | ||
77 | response.setHeader("1728000", "Access-Control-Max-Age"); | ||
78 | return response; | ||
79 | } | ||
80 | |||
81 | {{/code}} | ||
82 | |||
83 | 2) Add a new route to handle the OPTIONS request. The route URI should be the same as the GET/POST/PUT/DELETE routes that you have. | ||
84 | |||
85 | {{code}} | ||
86 | |||
87 | restRequestHandler.insertRoute(new ERXRoute(Member.ENTITY_NAME,"/members/index", ERXRoute.Method.Get,MembersController.class, "index")); | ||
88 | restRequestHandler.insertRoute(new ERXRoute(Member.ENTITY_NAME,"/members/index", ERXRoute.Method.Options,MembersController.class, "options")); | ||
89 | |||
90 | {{/code}} | ||
91 | |||
92 | Now you also need to add the Origin header into the response of your GET/POST/PUT/DELETE, or else XMLHttpRequest will still complain. To do that, you need to cast the response as a WOResponse and add the header. So instead of doing : | ||
93 | |||
94 | {{code}} | ||
95 | |||
96 | return response(editingContext(), Member.NomEntite, members, erxFilter()); | ||
97 | |||
98 | {{/code}} | ||
99 | |||
100 | You have to : | ||
101 | |||
102 | {{code}} | ||
103 | |||
104 | WOResponse myResponse = response(editingContext(), Member.NomEntite, members, erxFilter()).generateResponse(); | ||
105 | myResponse.setHeaders(this.request().headersForKey("Origin"), "Access-Control-Allow-Origin"); | ||
106 | return myResponse; | ||
107 | |||
108 | {{/code}} | ||
109 | |||
110 | == Dates == | ||
111 | |||
112 | The default formatter for dates is : | ||
113 | |||
114 | {{code}} | ||
115 | |||
116 | %Y-%m-%dT%H:%M:%SZ | ||
117 | |||
118 | {{/code}} | ||
119 | |||
120 | If you want to work with the the GMT offset, you have to use this instead : | ||
121 | |||
122 | {{code}} | ||
123 | |||
124 | %Y-%m-%dT%H:%M:%S%z | ||
125 | |||
126 | {{/code}} | ||
127 | |||
128 | To change it, you have to set the "er.rest.timestampFormat" property : | ||
129 | |||
130 | {{code}} | ||
131 | |||
132 | er.rest.timestampFormat = %Y-%m-%dT%H:%M:%S%z | ||
133 | |||
134 | {{/code}} | ||
135 | |||
136 | If you are using Dojo, you can use dojo.date.stamp.toISOString and dojo.date.stamp.fromISOString to convert from or to a Java date object. |