ERRest and Dojo

44
ERRest and Dojo Pascal Robert

Transcript of ERRest and Dojo

Page 1: ERRest and Dojo

ERRest and DojoPascal Robert

Page 2: ERRest and Dojo

• The basics

• Security

• Dojo and ERRest

• Using Dojo's DataGrid and JsonRestStore

• Client-side validation with JSON Schema

• Transactions

• Todo

Overview

Page 3: ERRest and Dojo

The basics

Page 4: ERRest and Dojo

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.

Page 5: ERRest and Dojo

HTTP methods and actions

HTTP method Action

GET Read (fetch)

POST Create

PUT Update

DELETE Delete

* This slide was taken from Mike's WOWODC 2009 session

Page 6: ERRest and Dojo

One gotcha : you need to use the HTTP adaptor from Wonder or WO 5.4 for DELETE and PUT

support.

Page 7: ERRest and Dojo

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 Mike's session from WOWODC West 2009 for more details on the architecture.

Page 8: ERRest and Dojo

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 Mike's WOWODC 2009 session

Page 9: ERRest and Dojo

ERXDefaultRouteController

• Provides abstract methods for all REST actions.

• One line registration in the REST request handler

Page 10: ERRest and Dojo

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 can't talk about

• Default format is XML

Page 11: ERRest and Dojo

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.

Page 12: ERRest and Dojo

DEMO

Page 13: ERRest and Dojo

Security

Page 14: ERRest and Dojo

Security

• You don't 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!

Page 15: ERRest and Dojo

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(); }

Page 16: ERRest and Dojo

Same Origin Policy

• XMLHttpRequest won't load (but will fetch) data if the XMLHttpRequest call is not in the same domain as the REST service, it's the Same Origin Policy.

• 4 options to get over that policy : window.name, HTTP Access Control, proxing or JSONP.

Page 17: ERRest and Dojo

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

Page 18: ERRest and Dojo

Same Origin Policy

To support HTTP Access Control, you have to add a route for the OPTIONS 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;}

Page 19: ERRest and Dojo

Same Origin Policy

You also need to set the headers in the response of your other actions :public WOActionResults indexAction() throws Throwable {... WOResponse response = response(editingContext(), Movie.ENTITY_NAME, movies, showFilter()).generateResponse(); response.setHeader("*", "Access-Control-Allow-Origin"); return response;}

Page 20: ERRest and Dojo

Same Origin Policy• window.name uses a iFrame to communicate with the REST

service, and the XMLHTTPRequest fetch the data locally from the iFrame window.name 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 don't support HTTP Access Control.

Page 21: ERRest and Dojo

Same Origin Policy

Adding window.name 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\">window.name='" + content + "';</script></html>"); response.setHeader("text/html", "Content-Type"); } return response;}

Page 22: ERRest and Dojo

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.

Page 23: ERRest and Dojo

DOJO

Page 24: ERRest and 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

Page 25: ERRest and Dojo

DOJO

• Lots of data widgets (Grid, Tree, etc.) and stores (JSON, Flickr, Picasa, etc.).

• Create a store that use the dojo.data 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.

Page 26: ERRest and Dojo

JsonRestStore

• Easy to use data store.

dojo.require("dojox.data.JsonRestStore");

var jsonStore = new dojox.data.JsonRestStore({ target: "/ra/movies" });

• store.save() 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; }

Page 27: ERRest and Dojo

DataGrid

• DataGrid displays data from a data store in a table.

• Columns are resizable and sortable.

• Paging is supported.

• Rows are editable.

Page 28: ERRest and Dojo

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

Page 29: ERRest and Dojo

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.

Page 30: ERRest and Dojo

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.

Page 31: ERRest and Dojo

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" } }}

Page 32: ERRest and Dojo

JSON Schema

• Wait... We already have the list of properties in the model...

• ... So we should generate the schema from the controller...

Page 33: ERRest and Dojo

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; }

Page 34: ERRest and Dojo

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(attribute.name(), property); } } } }

Page 35: ERRest and Dojo

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 dojox.data.JsonRestStore({ target: "/cgi-bin/WebObjects/Movies.woa/-6100/ra/movies", schema: theSchema });</script>

Page 36: ERRest and Dojo

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.

Page 37: ERRest and Dojo

Transactions

• When store.save() 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!

Page 38: ERRest and Dojo

DEMO

Page 39: ERRest and Dojo

Todo

• JSON Referencing

• HTML5 Storage

• Service Mapping Description (SMD)

Page 40: ERRest and Dojo

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" }}

• http://www.sitepen.com/blog/2008/11/21/effective-use-of-jsonreststore-referencing-lazy-loading-and-more/

Page 41: ERRest and Dojo

HTML5 Storage

• Dojo 1.5 have support for HTML5 local storage, but I didn't 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.

Page 42: ERRest and Dojo

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"} }}

• http://www.sitepen.com/blog/2008/03/19/pluggable-web-services-with-smd/

Page 44: ERRest and Dojo

Q&A