Child pages
  • RESTful Web Services

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin
{div:style=}
Wiki Markup
Div
styledisplay:none

DO

NOT

EDIT

THE

CONTENT

OF

THIS

PAGE

DIRECTLY

(EXCEPT

INSIDE

THE

DIV

BELOW

WITH

ID=DESCRIPTION),

UNLESS

YOU

KNOW

WHAT

YOU'RE

DOING.


THE

STRUCTURE

OF

THE

CONTENT

IS

VITAL

IN

BEING

ABLE

TO

AUTO

UPDATE

THE

CONTENT

THROUGH

THE

DOC

GENERATOR.

\\ \\ Enter additional information related to this



Enter additional information related to this 'class'

inside

the

\

{div}

macro

with

'id=description'

{div} {div:id=description}Using the RESTful Web Service plugin business logic can be exposed as a RESTful Web Service.  h1. About RESTful Web Services RESTful Web Services utilize the features of the HTTP Protocol to provide the API of the Web Service. For example, it uses the HTTP Request Types to indicate the type of operation: || Operation || HTTP Request Type || | Retrieving of existing records | GET | | Creating new records | POST | | Removing records | DELETE | | Updating existing records| PUT | Using these 4 HTTP Request Types a RESTful API mimics the CRUD operations (Create, Read, Update & Delete) common in transactional systems. A defining feature of REST is that it is stateless: each call the to a RESTful Web Service is completely stand-alone: it has no knowledge of previous requests. h1. Implementing a RESTful Web Service in Servoy The RESTful Web Service plugin does not contain any client side functions or properties, it is a 100% server side operating plugin. A RESTful Web Service can be created by creating a Form in a solution and implement one or more of the following methods to the Form: || Method name || HTTP Request Type || Description || | ws_read | GET | Used for the retrieval of data | | ws_create | POST | Used for the creation of new records | | ws_delete | DELETE | Used for the removal of data | | ws_update | PUT | Used for updating data| | ws_authenticate | N/A | Used to authenticate the requesting client | | ws_response_headers | N/A | Used for setting HTTP headers in the outgoing response | *Implement ws_read():Object to allow data retrieval* By performing an HTTP GET on the url \

Div
iddescription

Using the RESTful Web Service plugin business logic can be exposed as a RESTful Web Service. 

About RESTful Web Services

RESTful Web Services utilize the features of the HTTP Protocol to provide the API of the Web Service. For example, it uses the HTTP Request Types to indicate the type of operation:

Operation

HTTP Request Type

Retrieving of existing records

GET

Creating new records

POST

Removing records

DELETE

Updating existing records

PUT

Using these 4 HTTP Request Types a RESTful API mimics the CRUD operations (Create, Read, Update & Delete) common in transactional systems.

A defining feature of REST is that it is stateless: each call the to a RESTful Web Service is completely stand-alone: it has no knowledge of previous requests.

Implementing a RESTful Web Service in Servoy

The RESTful Web Service plugin does not contain any client side functions or properties, it is a 100% server side operating plugin.

A RESTful Web Service can be created by creating a Form in a solution and implement one or more of the following methods to the Form:

Method name

HTTP Request Type

Description

ws_read

GET

Used for the retrieval of data

ws_create

POST

Used for the creation of new records

ws_delete

DELETE

Used for the removal of data

ws_update

PUT

Used for updating data

ws_authenticate

N/A

Used to authenticate the requesting client

ws_response_headers

N/A

Used for setting HTTP headers in the outgoing response

Implement ws_read():Object to allow data retrieval
By performing an HTTP GET on the url {serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName},

...

the

...

ws_read()

...

method

...

will

...

be

...

invoked.

...


The

...

method

...

must

...

return

...

either

...

a

...

JavaScript

...

object

...

or

...

a

...

byte

...

array.

...

If

...

the

...

return

...

value

...

is

...

a

...

JavaScript

...

object,

...

it

...

will

...

be

...

serialized

...

and

...

then

...

placed

...

in

...

the

...

body

...

of

...

the

...

HTTP

...

Response.

...

If

...

the

...

return

...

value

...

of

...

the

...

method

...

is

...

null,

...

a

...

NOT_FOUND

...

response

...

(HTTP

...

404)

...

will

...

be

...

generated.

...



Implement ws_create(content):Object

...

to

...

allow

...

adding

...

data

...


By

...

performing

...

an

...

HTTP

...

POST

...

on

...

the

...

url

...

{serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName},

...

the

...

ws_create()

...

method

...

will

...

be

...

invoked.

...

Data

...

has

...

to

...

be

...

supplied

...

in

...

the

...

body

...

of

...

the

...

HTTP

...

Request.

...


The

...

method

...

can

...

return

...

a

...

JavaScript

...

object

...

or

...

a

...

byte

...

array.

...

If

...

the

...

return

...

value

...

is

...

a

...

JavaScript

...

object,

...

it

...

will

...

be

...

serialized

...

and

...

then

...

placed

...

in

...

the

...

body

...

of

...

the

...

HTTP

...

Response.

...



Implement ws_delete():Boolean

...

to

...

allow

...

data

...

removal

...


By

...

performing

...

an

...

HTTP

...

DELETE

...

on

...

the

...

url

...

{serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName},

...

the

...

ws_delete()

...

method

...

will

...

be

...

invoked.

...


The

...

method

...

has

...

to

...

return

...

a

...

Boolean

...

value:

...


true:

...

to

...

indicate

...

successful

...

deletion.

...

This

...

result

...

will

...

generate

...

an

...

OK

...

response

...

(HTTP

...

200)

...


false:

...

to

...

indicate

...

delete

...

failure.

...

This

...

response

...

will

...

generate

...

a

...

NOT_FOUND

...

response

...

(HTTP

...

404)

...



Implement ws_update(content):Boolean

...

to

...

allow

...

updating

...

existing

...

data

...


By

...

performing

...

an

...

HTTP

...

PUT

...

on

...

the

...

url

...

{serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName},

...

the

...

ws_update()

...

method

...

will

...

be

...

invoked.

...

Data

...

has

...

to

...

be

...

supplied

...

in

...

the

...

body

...

of

...

the

...

HTTP

...

request.

...


The

...

method

...

has

...

to

...

return

...

a

...

Boolean

...

value:

...


true:

...

to

...

indicate

...

successful

...

update.

...

This

...

result

...

will

...

generate

...

an

...

OK

...

response

...

(HTTP

...

200)

...


false:

...

to

...

indicate

...

update

...

failure.

...

This

...

response

...

will

...

generate

...

a

...

NOT_FOUND

...

response

...

(HTTP

...

404)

...



Implement ws_authenticate(user,

...

password):Object

...

to

...

allow

...

authentification

...


If

...

present,

...

it

...

will

...

be

...

called

...

before

...

performing

...

any

...

rest

...

operation

...

(ws_read,

...

ws_create,

...

ws_delete,

...

ws_update).

...


If

...

the

...

method

...

returns

...

false

...

or

...

null

...

an

...

UNAUTHORIZED

...

response

...

is

...

generated

...

(HTTP

...

401).

...

Any

...

other

...

return

...

value

...

will

...

be

...

added

...

as

...

a

...

request

...

parameter

...

with

...

name

...

"ws_authenticate"

...

to

...

the

...

incoming

...

request.

...



Implement ws_response_headers():Object

...

to

...

allow

...

setting

...

response

...

headers

...


The

...

method

...

can

...

return

...

either

...

a

...

string

...

or

...

an

...

array

...

of

...

strings

...

of

...

type

...

"headerKey=headerValue".

...

These

...

will

...

be

...

set

...

in

...

the

...

response

...

header.

...



In case the matching method for the specific HTTP operation (GET,

...

POST,

...

DELETE

...

or

...

PUT)

...

does

...

not

...

exists

...

on

...

the

...

form,

...

an

...

INTERNAL_SERVER_ERROR

...

response

...

(HTTP

...

500)

...

will

...

be

...

generated.

...

Request

...

parameters

...

Additional

...

parameters

...

can

...

be

...

specified

...

in

...

the

...

URL

...

of

...

the

...

HTTP

...

Requests.

...

There

...

are

...

two

...

variations

...

and

...

how

...

they

...

are

...

forwarded

...

to

...

the

...

ws_*

...

methods

...

differs.

...

Additional

...

URL

...

path

...

elements

...


The

...

base

...

URL

...

for

...

each

...

operation

...

is

...

{serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName}.

...

Additional

...

arguments

...

can

...

be

...

specified

...

by

...

adding

...

to

...

the

...

URL

...

path:

...

Code Block

...

{serverUrl}/servoy-service/rest_ws/{solutionName}/{formName}/{someValue}/{anotherValue}

...

The

...

additional

...

URL

...

path

...

elements

...

{someValue}

...

and

...

{anotherValue}

...

will

...

be

...

passed

...

into

...

the

...

ws_*

...

method

...

as

...

additional

...

arguments.

...

This

...

means

...

that

...

for

...

ws_read()

...

and

...

ws_delete()

...

they

...

will

...

be

...

the

...

first

...

and

...

second

...

argument

...

and

...

for

...

ws_create()

...

and

...

ws_update()

...

they

...

will

...

be

...

the

...

2nd

...

and

...

3rd

...

argument,

...

as

...

these

...

method

...

already

...

have

...

by

...

default

...

the

...

content

...

of

...

the

...

request

...

as

...

first

...

argument.

...

Query

...

parameters

...


The

...

request

...

URLs

...

can

...

also

...

be

...

extended

...

with

...

Query

...

parameters:

...

{serverUrl}/servoy-service/rest_ws/

...

{solutionName}/

...

{formName}?

...

{someKey}=

...

{someValue}&

...

{anotherKey}=

...

{anotherValue}&&

...

{anotherKey}=

...

{anotherValue2}

...

If

...

the

...

URL

...

contains

...

Query

...

parameters,

...

these

...

will

...

be

...

passed

...

into

...

the

...

ws_*

...

method

...

as

...

an

...

additional

...

last

...

argument.

...

This

...

last

...

argument

...

is

...

a

...

JavaScript

...

object

...

containing

...

all

...

keys

...

as

...

properties

...

with

...

the

...

values

...

associated

...

with

...

the

...

key

...

in

...

a

...

Array:

...

Object<Array<String>>

...

Example
Additional URL path elements and Query parameters can be combined in the URL (the query parameters should come after the additional URL path elements):

Code Block
{serverUrl}/servoy-service/rest_ws/{solutionName}/{formName}/{someValue}/{anotherValue}?{someKey}={someValue}&{anotherKey}={anotherValue}&&{anotherKey}={anotherValue2}

...

A

...

HTTP

...

Get

...

Request

...

on

...

url

...

{serverUrl}/servoy-service/rest_ws/myRestAPISolution/APIv1/foo/bar?name=John&age=30&pet=Cat&pet=Dog

...

would

...

result

...

in

...

invoking

...

the

...

ws_read

...

method

...

on

...

form

...

'APIv1'

...

of

...

solution

...

'myRestAPISolution'.

...

The

...

ws_read

...

function

...

will

...

be

...

invoked

...

with

...

three

...

parameters:

...

'foo',

...

'bar',

...

{name:

...

'John'

...

,

...

age:

...

30

...

,

...

pet:

...

'Cat',

...

'Dog'

...

}

...

Code Block

...


function ws_read()
{
  for (var i = 0; arguments.length, i++) {
      if (typeof arguments[i] == 'String') { //The URL path additions are passed in as Strings
           application.output('URL Path addition: ' + arguments[i])
      } else {
           for each (var key in arguments[i]) {
                application.output('Query Parameter "' + key + '", values:  "' + arguments[i][key].join(', ') + '"')
           }
      }
  }
}

//outputs:
//URL Path addition: foo
//URL Path addition: bar
//Query Parameter "name", values:  "John"
//Query Parameter "age", values:  "30"
//Query Parameter "pet", values:  "Cat, dog"

...

Stateless

RESTful Web Services are to be stateless. As subsequent requests to the RESTful Web Service API might be handled by different headless clients in the pool of clients configured for the plugin, do not use any state in between calls to the API. This means at least the following:

  • Do not use global or form variables
  • Do not use the solution model API
  • Do not alter the state of the a form's UI
  • Do save or rollback any unsaved changes before the end of the method

HTTP Request

For the POST and PUT operation (resp. ws_create() and ws_update() methods), data has to be supplied in the body of the HTTP Request. If the content in the body of the HTTP Request is missing, a NO_CONTENT response will be generates (HTTP 204).

The supported Content-Types are JSON (application/json), XML (application/xml) and byte array (application/binary). The Content-Type can be explicitly set in the header of the HTTP Request:

Code Block
Content-Type: application/json; charset=utf-8

...

Code Block
Content-Type: application/xml; charset=utf-8

...

Code Block
Content-Type: application/binary

...

Note:

...

the

...

charset

...

is

...

optional.

...

If

...

not

...

specified,

...

utf-8

...

will

...

be

...

assumed.

...

If

...

no

...

valid

...

Content-Type

...

is

...

set,

...

the

...

plugin

...

will

...

try

...

to

...

establish

...

the

...

type

...

of

...

the

...

content

...

based

...

on

...

the

...

first

...

character

...

of

...

the

...

content:

...

  • '

...

  • {':

...

  • Content-Type

...

  • application/json

...

  • will

...

  • be

...

  • assumed
  • '<':

...

  • Content-Type

...

  • application/xml

...

  • will

...

  • be

...

  • assumed

...

When

...

the

...

Content-Type

...

cannot

...

be

...

determined,

...

an

...

UNSUPPORTED_MEDIA_TYPE

...

response

...

will

...

be

...

generated

...

(HTTP

...

415).

...

The

...

supplied

...

value

...

in

...

the

...

body

...

of

...

the

...

HTTP

...

request

...

will

...

be

...

applied

...

as

...

argument

...

to

...

the

...

invocation

...

of

...

the

...

method.

...

If

...

the

...

body

...

Content-Type

...

is

...

not

...

application/binary,

...

it

...

will

...

be

...

converted

...

to

...

a

...

JavaScript

...

object

...

automatically.

...

If

...

the

...

body

...

content

...

cannot

...

be

...

converted

...

to

...

a

...

JavaScript

...

object

...

based

...

on

...

the

...

Content-Type

...

an

...

INTERNAL_SERVER_ERROR

...

response

...

(HTTP

...

500)

...

will

...

be

...

generated.

...

HTTP

...

Response

...

By

...

default,

...

the

...

plugin

...

will

...

respond

...

with

...

the

...

same

...

Content

...

Type

...

as

...

was

...

specified

...

in

...

the

...

HTTP

...

Request.

...

This

...

can

...

be

...

overruled

...

by

...

specifying

...

a

...

different

...

response

...

Content-Type

...

in

...

the

...

Accept

...

header

...

of

...

the

...

HTTP

...

Request:

...

Code Block

...

Accept: application/json

...

Code Block
Accept: application/binary

...

By

...

default,

...

the

...

response

...

will

...

be

...

encoded

...

with

...

the

...

UTF-8

...

charset,

...

if

...

the

...

response

...

Content-Type

...

is

...

not

...

application/binary

...

with

...

byte

...

array

...

as

...

response.

...

If

...

the

...

HTTP

...

Request

...

specified

...

a

...

different

...

encoding,

...

that

...

will

...

be

...

used

...

instead.

...

If

...

the

...

encoding

...

of

...

the

...

response

...

needs

...

to

...

be

...

different

...

than

...

the

...

request

...

encoding,

...

this

...

can

...

be

...

specified

...

in

...

the

...

HTTP

...

Request

...

by

...

setting

...

the

...

charset

...

value

...

in

...

the

...

Accept

...

header:

...

Code Block

...

Accept: application/json; charset=UTF-16

...

Returning custom status codes

While some of the HTTP Response status codes are hardcoded in the RESTful Webservices plugin (see this documentation), it is possible to control the HTTP Status codes from within the ws_* methods. Returning a custom HTTP Status Code can be done by throwing the specific value (a number) for the HTTP Status Code.

For example, when a ws_update call comes in for a non-existing resource, the HTTP Status Code to return would be a "Not Found" status code, which is a 404. Returning the 404/Not Found HTTP Status code from within a ws_* method could be done the following way:

Code Block
function ws_update(){
    //your logic here
    throw 404;
}

...

For convenience purposes,

...

all

...

available

...

HTTP

...

Status

...

Codes

...

are

...

also

...

listed

...

under

...

the

...

HTTP

...

Plugin

...

shipped

...

with

...

Servoy,

...

so

...

instead

...

of

...

throwing

...

the

...

number

...

404

...

in

...

the

...

previous

...

example,

...

a

...

more

...

readable

...

way

...

would

...

be

...

to

...

throw

...

plugins.http.HTTP_STATUS.SC_NOT_FOUND

...

See

...

List_of_HTTP_status_

...

codes on Wikipedia for additional information on all status codes

It is also possible to append a detailed description for the returned status code

Code Block

function ws_update(){
    //your logic here
    throw [plugins.http.HTTP_STATUS.SC_UNAUTHORIZED, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><error><reason>access denied</reason><message>access token is expired</message></error>"];

...

Authentication/Authorization

...

The

...

RESTful

...

Web

...

service

...

plugin

...

can

...

be

...

configured

...

to

...

check

...

authentication/authorisation.

...


The

...

plugins

...

server

...

property

...

rest_ws_plugin_authorized_groups

...

can

...

be

...

set

...

with

...

a

...

comma

...

separated

...

list

...

of

...

groups

...

defined

...

in

...

the

...

built-in

...

security

...

system

...

of

...

Servoy.

...


When

...

the

...

property

...

is

...

filled

...

with

...

usergroups,

...

HTTP

...

Basic

...

authentication

...

is

...

enabled

...

for

...

all

...

webservice

...

requests.

...

The

...

username/password

...

supplied

...

in

...

the

...

HTTP

...

Request

...

is

...

validated

...

against

...

the

...

users

...

registered

...

in

...

Servoy's

...

built-in

...

security

...

system

...

and

...

additionally

...

against

...

group

...

membership.

...

Access

...

is

...

denied

...

if

...

the

...

user

...

does

...

not

...

exists

...

or

...

the

...

supplied

...

password

...

is

...

incorrect,

...

or

...

the

...

user

...

doesn't

...

belong

...

to

...

one

...

of

...

the

...

specified

...

groups.

...

When

...

access

...

is

...

denied,

...

an

...

UNAUTHORIZED

...

response

...

is

...

generated

...

(HTTP

...

401).

...

JSONP

...

support

...

The

...

plugin

...

supports

...

so-called

...

JSONP:

...

a

...

variation

...

of

...

JSON

...

that

...

allows

...

cross

...

domain

...

data

...

retrieval.

...

The

...

JSONP

...

variation

...

can

...

be

...

invoked

...

by

...

added

...

a

...

"callback"

...

parameter

...

to

...

the

...

HTTP

...

Request

...

URL:

...

Code Block

...


http://domain:port/servoy-service/rest_ws/\{solutionName}/\{formName}?callback=\{callbackFunctionName}

...

When

...

invoked

...

with

...

the

...

value

...

"myCallback"

...

for

...

the

...

"callback"

...

parameter,

...

the

...

returned

...

JSON

...

value

...

will

...

be

...

wrapped

...

in

...

a

...

function

...

call

...

to

...

the

...

"myCallback"

...

function:

...

Code Block

...


myCallback({ "hello" : "Hi, I'm JSON. Who are you?"})

...

Pool of Clients

To service the requests to the RESTful Web service API, the plugin creates a pool of (headless) clients. The maximum number of clients allowed can be set using the "rest_ws_plugin_client_pool_size"

...

property

...

of

...

the

...

plugin

...

(default

...

value

...

=

...

5).

...

When

...

there

...

are

...

more

...

concurrent

...

requests

...

than

...

the

...

number

...

of

...

clients

...

in

...

the

...

pool,

...

by

...

default

...

the

...

requests

...

will

...

wait

...

until

...

a

...

client

...

becomes

...

available

...

in

...

the

...

pool.

...

This

...

behavior

...

can

...

be

...

altered

...

by

...

setting

...

the

...

"rest_ws_plugin_client_pool_exhausted_action"

...

property

...

of

...

the

...

plugin.

...

The

...

following

...

values

...

are

...

supported

...

for

...

this

...

property:

...

  • block

...

  • (default):

...

  • requests

...

  • will

...

  • wait

...

  • untill

...

  • a

...

  • client

...

  • becomes

...

  • available

...

  • fail:

...

  • the

...

  • request

...

  • will

...

  • fail.

...

  • The

...

  • API

...

  • will

...

  • generate

...

  • a

...

  • SERVICE_UNAVAILABLE

...

  • response

...

  • (HTTP

...

  • 503)

...

  • grow:

...

  • allows

...

  • the

...

  • pool

...

  • to

...

  • temporarily

...

  • grow,

...

  • by

...

  • starting

...

  • additional

...

  • clients.

...

  • These

...

  • will

...

  • be

...

  • automatically

...

  • removed

...

  • when

...

  • not

...

  • required

...

  • anymore.

...

Note

...

title

...

Servoy

...

Cluster

...

The

...

RESTful

...

Web

...

service

...

plugin

...

uses

...

a

...

pool

...

of

...

headless

...

clients

...

to

...

service

...

the

...

requests.

...

When

...

operated

...

within

...

a

...

Servoy

...

Cluster,

...

note

...

that

...

poolsize

...

is

...

set

...

per

...

Servoy

...

Application

...

Server.

...

Samples

A sample solution is included in the Servoy distribution (servoy_sample_rest_ws.servoy),

...

detailing

...

how

...

to

...

retrieve

...

data

...

from

...

the

...

http

...

request

...

and

...

to

...

return

...

a

...

response.

...

For

...

more

...

information

...

on

...

RESTful

...

Web

...

Services,

...

see:

...

http://en.wikipedia.org/wiki/Representational_State_Transfer

...


http://www.infoq.com/articles/rest-introduction

...


http://www.ibm.com/developerworks/webservices/library/ws-restful/

...


http://home.ccil.org/~cowan/restws.pdf

...


HTML Table
id
classservoy sSummary
Colgroup Tag
Column
padding0px
width80px

Column

Table Row (tr)
styleheight: 30px;
Table Head (th)
colspan2
Server Property Summary
Table Body (tbody)
Table Row (tr)
Table Cell (td)

Table Cell (td)
#rest_ws_plugin_authorized_groups

...

Table Body (tbody)
Table Row (tr)
Table Cell (td)

Table Cell (td)
#rest_ws_plugin_client_pool_exhausted_action

...

Table Body (tbody)
Table Row (tr)
Table Cell (td)

Table Cell (td)
#rest_ws_plugin_client_pool_size

...


HTML Table
idserverProperty
classservoy sDetail
Colgroup Tag
Column
padding0px
width100%

Table Row (tr)
styleheight: 30px;
Table Head (th)
colspan1
Server Property Details
Table Body (tbody)
idrest_ws_plugin_authorized_groups
}{
Table Row (tr)
:
id
=
name
}{td}h6.
Table Cell (td)
rest_ws_plugin_authorized_groups
{td}{tr}{tr:class=lastDetailRow}{td}{td}{tr}{tbody}{tbody:id=
Table Row (tr)
classlastDetailRow
Table Cell (td)

Table Body (tbody)
idrest_ws_plugin_client_pool_exhausted_action
}{
Table Row (tr)
:
id
=
name
}{td}h6.
Table Cell (td)
rest_ws_plugin_client_pool_exhausted_
action{td}{tr}{tr:class=lastDetailRow}{td}{td}{tr}{tbody}{tbody:id=
action
Table Row (tr)
classlastDetailRow
Table Cell (td)

Table Body (tbody)
idrest_ws_plugin_client_pool_size
}{
Table Row (tr)
:
id
=
name
}{td}h6.
Table Cell (td)
rest_ws_plugin_client_pool_size
{td}{tr}{tr:class=lastDetailRow}{td}{td}{tr}{tbody}{table}
Table Row (tr)
classlastDetailRow
Table Cell (td)