ERRest and Dojo
Upcoming SlideShare
Loading in...5

ERRest and Dojo






Total Views
Views on SlideShare
Embed Views



0 Embeds 0

No embeds



Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
Post Comment
Edit your comment

ERRest and Dojo ERRest and Dojo Presentation Transcript

  • ERRest and DojoPascal Robert
  • Overview• The basics• Security• Dojo and ERRest • Using Dojos DataGrid and JsonRestStore • Client-side validation with JSON Schema • Transactions• Todo
  • The basics
  • The basics• Think of REST as direct actions with specific action per HTTP methods (GET, POST, etc.).• ERRest will create/modify EOs based on data sent by the client, not need to call request().formValueForKey().• No envelope on the request, if meta-data is needed, it will use the HTTP headers or parameters in the URL.
  • HTTP methods and actions HTTP method Action GET Read (fetch) POST Create PUT Update DELETE Delete* This slide was taken from Mikes WOWODC 2009 session
  • One gotcha : you need to use the HTTP adaptorfrom Wonder or WO 5.4 for DELETE and PUT support.
  • Basic architecture of ERRest• ERRest uses the same architecture as the REST support in Ruby on Rails.• You build controllers for each entity you want to offer as REST services.• You create routes that you register in the REST request hander, and the routes are connected to controllers.• Check Mikes session from WOWODC West 2009 for more details on the architecture.
  • Routes and actions HTTP method URL Action GET /movies fetch all movies GET /movies/100 fetch movie with id 100 POST /movies create new movie object PUT /movies/100 update movie id 100 DELETE /movies/100 delete movie id 100* This slide was taken from Mikes WOWODC 2009 session
  • ERXDefaultRouteController• Provides abstract methods for all REST actions.• One line registration in the REST request handler
  • ERXRestFormat• Parse the request and write the response in different formats : • JSON • JS (same as JSON, but with a different MIME type) • ASCII plist (iPhone/OS X apps!) • XML • SproutCore (JSON with specific properties) • Rails (XML, but with a different format) • Some chocolate and nuts framework that we cant talk about• Default format is XML
  • Filtering• You may not want to return all attributes and all relationships attributes in your response.• Filtering works with ERXKeyFilter.• You can have an different filter per route.
  • DEMO
  • Security
  • Security• You dont want anyone to be able to delete any objects...• The most popular way to do this is with "authkeys".• Ask users to send the key in the URL...• ... and use request().stringFormValueForKey to get it• Use HTTPS when using auth keys!
  • Security protected void checkAuthKey() throws SecurityException { String pw = context().request().stringFormValueForKey("pw"); if(!("mySecretKey".equals(pw))) { throw new SecurityException("Invalid authorization key"); } } public WOActionResults indexAction() throws Throwable { checkAuthKey(); return response(editingContext(), MyEOClass.ENTITY_NAME, movies, showFilter()).generateResponse(); }
  • Same Origin Policy• XMLHttpRequest wont load (but will fetch) data if the XMLHttpRequest call is not in the same domain as the REST service, its the Same Origin Policy.• 4 options to get over that policy :, HTTP Access Control, proxing or JSONP.
  • Same Origin Policy• HTTP Access Protocol sends a OPTIONS HTTP call to the service with specific headers. • Access-Control-Request-Method • Access-Control-Request-Headers • Origin• REST service must reply to the OPTIONS request with some options• Supported by Safari 4+, Firefox 3.5+, IE 8 and Chrome 5
  • Same Origin PolicyTo support HTTP Access Control, you have to add a route for theOPTIONS method:restReqHandler.insertRoute(new ERXRoute(Movie.ENTITY_NAME,"/movies",ERXRoute.Method.Options,MoviesController.class, "options"));Add the optionsAction method in your controller:public WOActionResults optionsAction() throws Throwable { ERXResponse response = new ERXResponse(); response.setHeader("*", "Access-Control-Allow-Origin"); response.setHeaders(this.request().headersForKey("Access-Control-Request-Method"), "Access-Control-Allow-Methods"); response.setHeaders(this.request().headersForKey("Access-Control-Request-Headers"), "Access-Control-Allow-Headers"); response.setHeader("1728000", "Access-Control-Max-Age"); return response;}
  • Same Origin PolicyYou also need to set the headers in the response of your otheractions :public WOActionResults indexAction() throws Throwable {... WOResponse response = response(editingContext(), Movie.ENTITY_NAME, movies, showFilter()).generateResponse(); response.setHeader("*", "Access-Control-Allow-Origin"); return response;}
  • Same Origin Policy• uses a iFrame to communicate with the REST service, and the XMLHTTPRequest fetch the data locally from the iFrame property. • Adds "windowname=true" to the query • Response must be text/html wrapped in script tag• JSONP is the same concept, but use a <script> tag instead of an iFrame.• Good alternative for older browsers that dont support HTTP Access Control.
  • Same Origin PolicyAdding supportpublic WOActionResults indexAction() throws Throwable {... String windowNameArg = this.request().stringFormValueForKey("windowname"); if ((windowNameArg != null) && ("true".equals(windowNameArg))) { String content = response.contentString().replaceAll("n", ""); response.setContent("<html><script type="text/javascript">" + content +";</script></html>"); response.setHeader("text/html", "Content-Type"); } return response;}
  • Same Origin Policy• Last option : proxing on the client side, where the client XMLHTTPRequest makes its request to a local service (WO, JSP, PHP, etc.) and that service connects to your service.
  • DOJO
  • DOJO• First release in March 2005• Managed by the Dojo Foundation, backed by IBM, Sun and others• Dual licensed : BSD and Academic Free• Latest release is 1.5, on July 15th 2010
  • DOJO• Lots of data widgets (Grid, Tree, etc.) and stores (JSON, Flickr, Picasa, etc.).• Create a store that use the system and it will be available for all data widgets like the DataGrid.• Switching to a different store might require only one line of change.
  • JsonRestStore• Easy to use data store. dojo.require(""); var jsonStore = new{ target: "/ra/movies" });• method will contact your REST service to save changes.• Must change the default format in your REST controller to JSON. protected ERXRestFormat defaultFormat() { return ERXRestFormat.JSON; }
  • DataGrid• DataGrid displays data from a data store in a table.• Columns are resizable and sortable.• Paging is supported.• Rows are editable.
  • DataGrid paging• In the data grid properties, add : rowsPerPage="a int value"• Dojo will send an extra header when paging is required : Range: 0-19• The REST service must reply with : Content-Range: 0-19/99
  • Adding new objects• We can add new objects to the store, they will be created server-side when the save() method is called on the store.• The new object have to be added to the store with the newItem () method.
  • JSON Schema• Define the structure of JSON data.• List of properties for the JSON object. Type can be string, number, integer, object, array, enum, etc.• You can reference other schemas, including schemas specified at full URLs.• Dojo can use it to perform client side validation.• We can call it WSDL for JSON, with less chat.
  • JSON Schema example { "name":"Movie", "properties":{ "title":{ "maxLength":255, "minLength":1, "type":"string" }, "dateReleased":{ "optional":true, "format":"date-time", "type":"string" }, "category":{ "maxLength":20, "optional":true, "type":"string" } }}
  • JSON Schema• Wait... We already have the list of properties in the model...• ... So we should generate the schema from the controller...
  • Schema generation public WOActionResults indexAction() throws Throwable {... String schemaArg = this.request().stringFormValueForKey("schema"); if (schemaArg != null) { return schemaAction(showFilter()); } } public WOActionResults schemaAction(ERXKeyFilter keyFilter) { HashMap rootObject = new HashMap(); rootObject.put("name", Movie.ENTITY_NAME); HashMap properties = new HashMap(); addSchemaProperties(properties, Movie.ENTITY_NAME, keyFilter); rootObject.put("properties", properties); WOResponse response = new WOResponse(); response.setHeader("application/json", "Content-Type"); response.setContent(JSONSerializer.toJSON(rootObject, ERXJSONRestWriter._config).toString()); return response; }
  • Schema generation protected void addSchemaProperties(HashMap properties, String entityName, ERXKeyFilter keyFilter) { EOEntity entity = EOModelGroup.globalModelGroup().entityNamed(entityName); EOClassDescription classDescription = ERXRestClassDescriptionFactory.classDescriptionForEntityName(entityName); for (String attributeName : (NSArray<String>) classDescription.attributeKeys()) { ERXKey<Object> key = new ERXKey<Object>(attributeName); if (keyFilter.matches(key, ERXKey.Type.Attribute)) { EOAttribute attribute = entity.attributeNamed(key.key()); if (attribute != null) { HashMap property = new HashMap(); if (attribute.allowsNull()) { property.put("optional", true); } if (attribute.className().equals("java.lang.String")) { property.put("type", "string"); if (attribute.width() > 0) { property.put("maxLength",attribute.width()); if ((property.get("optional") == null) || (!(Boolean)property.get("optional"))) { property.put("minLength",1); } } }... properties.put(, property); } } } }
  • Schema generation <script type="text/javascript"> var theSchema; function fetchSchema() { var xhrArgs = { url: "/cgi-bin/WebObjects/Movies.woa/-6100/ra/movies?schema=true", handleAs: "json", sync: true, load: function(data){ theSchema = data; }, error: function(error){ console.log = "An unexpected error occurred: " + error; } } dojo.xhrGet(xhrArgs); } fetchSchema(); var jsonStore = new{ target: "/cgi-bin/WebObjects/Movies.woa/-6100/ra/movies", schema: theSchema });</script>
  • JSON Schema• With JSON schema, when adding new items to the store, validation will be done client-side, no need to contact the server for basic validation.
  • Transactions• When is called, if multiple objects have to be saved, Dojo will add a Transaction, a Seq-Id and a Client-Seq-Id headers.• The value of the header is either Open or Commit.• Seq-Id is a integer with the transaction number.• Client-Seq-Id is a unique id per browser page.• Only one problem : transactions are not sent in order!• Let see this in action!
  • DEMO
  • Todo• JSON Referencing• HTML5 Storage• Service Mapping Description (SMD)
  • JSON Referencing• Instead of putting the content of related objects in the root object, you put references to those objects.• Dojo will do lazy-loading of the related objects, eg it will fetch them when needed.• Data will look like this : { "title": "title", "studio": {"$ref": "/studio/1" } }• and-more/
  • HTML5 Storage• Dojo 1.5 have support for HTML5 local storage, but I didnt find the documention for it.• Previous Dojo versions have OfflineRest, but it uses Flash (doh!) as the data storage engine for WebKit browsers, and Google Gears on Firefox.
  • Service Mapping Description• SMD describes available services, just like WSDL for SOAP.• JSON Schema is part of SMD.• Look like this: { target:"/jsonrpc", transport:"POST", envelope:"JSON-RPC-1.2", SMDVersion:"2.0", services: { add : { // define a service to add two numbers parameters: [ {name:"a",type:"number"}, // define the two parameters {name:"b",type:"number"} ], returns:{"type":"number"} } }•
  • Resources•••••
  • Q&A