48
MONTREAL JUNE 30, JULY 1ST AND 2ND 2012 ERRest in Depth Pascal Robert MacTI.ca

ERRest in Depth

Embed Size (px)

DESCRIPTION

See how the request/response loop works in a ERRest context, and how to change the behavior of your REST services built with Project Wonder.

Citation preview

Page 1: ERRest in Depth

MONTREAL JUNE 30, JULY 1ST AND 2ND 2012

MONTREAL JUNE 30, JULY 1ST AND 2ND 2012

ERRest in DepthPascal RobertMacTI.ca

Page 2: ERRest in Depth

• Request/response loop

• Behavior changes

• Formats

• Transactions

• Same Origin Policy

• Date and time management

The Menu

Page 3: ERRest in Depth

Request handling

Page 4: ERRest in Depth

Request

Application.dispatchRequest

ERXRouteRequestHandler(WOActionRequestHandler).handleRequest

YourController(ERXRouteController).performActionNamed

Page 5: ERRest in Depth

performActionNamed

Page 6: ERRest in Depth

checkAccess()

• Default implementation in ERXRouteController does nothing

• Override it in your controller for security check

Page 7: ERRest in Depth

performHtmlActionNamed

• Does <EntityName><ActionName>Page component exists?

• No: fall back to controller

• Yes: Check if component implements IEXRouteComponent

• Yes: return the component

• No: fall back to controller

Page 8: ERRest in Depth

shouldFailOnMissingHtmlPage

• Does the component was not found or don't implement IEXRouteComponent?

• If shouldFailOnMissingHtmlPage() returns true, call performUnknownAction (will return a 404 NotFound)

• Default is false, override it in your controller if needed.

Page 9: ERRest in Depth

performRouteActionNamed

• Try to find <actionName>Action method.

• Not found? Try to find <actionName> method.

• Still nothing? Check for annotations.

• Still nothing? Call performUnknownAction

• Got something? Call performActionWithArguments

Page 10: ERRest in Depth

performUnknownAction

• if (ERXRest.strictMode)

• throw ERXNotAllowedException (HTTP code 405)

• else

• throw FileNotFoundException (HTTP code 404)

Page 11: ERRest in Depth

performActionWithArguments

• Will invoke the method with the arguments

Page 12: ERRest in Depth

Objects management

Page 13: ERRest in Depth

Objects in routes

• /ra/<entityName>/{entity:EntityName}

• routeObjectForKey(key)

• create(filter)

• update(object, filter)

Page 14: ERRest in Depth

routeObjectForKey

• {<keyName>:<keyType>} in route = keyType result = routeObjectForKey(<keyName>)

• Object is obtained by ERXRestUtils.coerceValueToTypeNamed

Page 15: ERRest in Depth

coerceValueToTypeNamed

• Where value is transformed to a object or primitive

• If it's an EO or POJO, will use ERXRestClassDescriptionFactory.classDescriptionForEntityName to find the class

• Will call IERXRestDelegate.Factory.delegateForClassDescription().objectOfEntityWithID()

Page 16: ERRest in Depth

create(filter)

• Will call ERXRestClassDescriptionFactory.classDescriptionForEntityName

• Will call IERXRestDelegate.Factory.delegateForClassDescription().createObjectOfEntityWithID to create a basic EO/POJO

• Will call updateObjectWithFilter()

Page 17: ERRest in Depth

update(object, filter)

• As with create(filter), will simply call updateObjectWithFilter

Page 18: ERRest in Depth

updateObjectWithFilter

• Major method

• Will take content from request and update object (PUT request)

• Also used to populate new object (POST request)

Page 19: ERRest in Depth

Response handling

Page 20: ERRest in Depth

response(object, filter)

• Will built up the object graph with ERXRestRequestNode.requestNodeWithObjectAndFilter

• Will call response(format, responseNode)

Page 21: ERRest in Depth

requestNodeWithObjectAndFilter

• If primitive/simple object, will set the value

• If EO/POJO, will call _fillInWithObjectAndFilter

Page 22: ERRest in Depth

_fillInWithObjectAndFilter

• If object is an array, method take itself

• If not array, will use object's delegate to call primaryKeyForObject, and call _addAttributesAndRelationshipsForObjectOfEntity

Page 23: ERRest in Depth

response(format, responseNode)

• Returns result of ERXRouteResults(context, restContext, format, responseNode)

• ERXRouteResults.generateResponse() will actually generate the response in requested format

Page 24: ERRest in Depth

response(ERXRestFetchSpecification, filter)

• Useful to return list of objects ("index" action)

• ERXRestFetchSpecification allow you to set ordering, range, filtering and batching from request

• Will also call response(format, responseNode)

Page 25: ERRest in Depth

response(int)

• Use that one to send a response without any content in the body

• Check ERXHttpStatusCodes

Page 26: ERRest in Depth

There's a property for that

Page 27: ERRest in Depth

"id" key

• ERXRest.idKey : what to use instead of "id"

Default: {"id":2 }

ERRest.idKey=primaryKey {"primaryKey":2 }

Page 28: ERRest in Depth

"nil" key

• ERXRest.nilKey

To rewrite the "nil" attribute

Default: <someAttribute nil="true"/>

ERXRest.nilKey=cestVide -> <someAttribute cestVide="true"/>

• ERXRest.writeNilKey

Skip the "nil" attribute

Default: <someAttribute nil="true"/>

ERXRest.writeNilKey=false -> <someAttribute nil="true"/>

Page 29: ERRest in Depth

"type" key

• ERXRest.typeKey

Allow you to change the name of the "type" attribute

Default: {"type":"NameOfEntity"}

ERXRest.typeKey=entityName {"entityName":"NameOfEntity"}

• ERXRest.writeTypeKey

If false, won't write the "type" attribute in the response

ERXRest.writeTypeKey=false -> {id: 2, "type":"NameOfEntity"}

Page 30: ERRest in Depth

ERXRest.pluralEntityNames

• Default is true

• If set to false, can't use pluralized names

Default: /ra/restEntities

Set to false: /ra/restEntities

Page 31: ERRest in Depth

ERXRest.suppressTypeAttributesForSimpleTypes

• Only for XML format

• Default value is false

• Default rendering:

<RestEntity primaryKey="1"> <someAttribute type = "integer">2</someAttribute></RestEntity>

• When set to false:

<RestEntity primaryKey="1"> <someAttribute type = "integer">2</someAttribute>

Page 32: ERRest in Depth

ERXRest.strictMode

• Default is: true

• For missing route, will send 405 (Not Allowed) code, if set to false, will send 404 (Not Found)

• POST requests: will send 201 (Created), if false will send 200 (OK)

Page 33: ERRest in Depth

ERXRest.routeCase

• ERXRest.routeCase=LowerCamelCase

/ra/restEntities

• ERXRest.routeCase=CamelCase

/ra/RestEntities

• ERXRest.routeCase=LowercaseUnderscore

/ra/rest_entities

Page 34: ERRest in Depth

ERXRest.parseUnknownExtensions

• /ra/restEntities/3.fsdfsd

• If set to true (the default):

HTTP/1.1 200 Apple WebObjects

Content-Type: text/html

• If set to false:

HTTP/1.0 400 Apple WebObjects

Page 35: ERRest in Depth

Formats

Page 36: ERRest in Depth

ERXRest.defaultFormat

• ERXRest.defaultFormat=json|xml|plist|html|...

• Let you specify the default format for all controllers

• Can override it per controller:

protected ERXRestFormat defaultFormat() {

return ERXRestFormat.json(); }

Page 37: ERRest in Depth

Format detection

• Format detection is in ERXRouteController.format()

• Order of detection

• From extension (.json). Set in ERXRouteRequestHandler.routeForMethodAndPath()

• From the Content-Type header

• Default format (defaultFormat() in controller)

Page 38: ERRest in Depth

Adding new format

• Your own private format? Use ERXRestFormat.registerFormatNamed()

• Format useful for the commumity? Add them to ERXRestFormat

Page 39: ERRest in Depth

Same Domain Policy

Page 40: ERRest in Depth

Same Origin Policy

• Problem: browsers won't load data if client and server not on same domain

• Numerous ways: window.name transport, JSONP and Cross-origin resource sharing (CORS)

• CORS and window.name transport support is part of ERRest

Page 41: ERRest in Depth

CORS

• Works with Gecko 1.9.1 (Firefox 3.5+), WebKit (Safari 4+, Google Chrome 3+), Opera 12 and IE 8+

• Send extra headers to server

• Client specifiy origin, requested HTTP verb and allowed headers, server returns allowed origin, methods, headers and max-age

Page 42: ERRest in Depth

CORS preflight

Client request:

OPTIONS /resources/post-here/ HTTP/1.1Origin: http://foo.example Access-Control-Request-Method: POST Access-Control-Request-Headers: X-PINGOTHER

Server response:

HTTP/1.1 200 OK Access-Control-Allow-Origin: http://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER Access-Control-Max-Age: 1728000

Client request:

GET /resources/post-here/ HTTP/1.1Origin: http://foo.example

Server response:

HTTP/1.1 200 OK Access-Control-Allow-Origin: http://foo.example

Page 43: ERRest in Depth

CORS

• ERXRest.accessControlAllowRequestHeaders

• ERXRest.accessControlAllowRequestMethods

• ERXRest.accessControlMaxAge (default: 1728000)

• ERXRest.accessControlAllowOrigin ('*' to allow all)

Page 44: ERRest in Depth

window.name Transport

• Dojo use that trick

• Client send ?windowname=true in URL

• Enable it on server with:

ERXRest.allowWindowNameCrossDomainTransport=true

• Will wrap response in HTML code:

<html><script type="text/javascript">window.name='{"id":4,"type":"RestEntity","someAttribute":"commit transaction"}';</script></html>

Page 45: ERRest in Depth

Status codes

Page 46: ERRest in Depth

Status code and exceptions

• ObjectNotAvailableException, FileNotFoundException, NoSuchElementException: returns a 404 (Not Found)

• SecurityException: returns a 403 (Forbidden)

• ERXNotAllowedException: returns a 405 (Method Not Allowed)

• ERXValidationException, NSValidation.ValidationException: returns a 400 (Bad Request)

• Anything else: returns a 500 (Internal Server Error)

• Avoid sending 5xx codes if the client made a mistake!

Page 47: ERRest in Depth

Adding support for new data types

• Add a processor in _ERXJSONConfig

• Add support code in ERXRestUtils

• isPrimitive()

• coerceValueToString()

• coerceValueToTypeNamed

• For dates: add formatter in ERXRestUtils

Page 48: ERRest in Depth

Q&A

MONTREAL JUNE 30, JULY 1ST AND 2ND 2012

MONTREAL JUNE 30, JULY 1ST AND 2ND 2012