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

ERRest in Depth

May 19, 2015

Download

Technology

WO Community

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.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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