Web Service ReST Custom Development API (Description)

Development (Description)
Table of Contents
Summary

Description of customized API for development of Web services following the  REST architecture.

This API is implemented with the internal direct use of ASP.NET HTTP handlers instead of using ASP.NET web-forms (.aspx files) avoiding this way the costs of the ASP.NET page life cycle (Please refer to here or here).

Description

The present REST API is implemented in the assembly “Web.Services.Rest.API.dll” and allows to develop web-services following a RESTful architecture, where:

Everything is a Resource

Every concept in the application is mapped to a resource and in turn mapped to an endpoint / resource identifer (Uri).

Actions: Basic CRUD operations

Every resource can support basic CRUD operations implemented through the following HTTP verbs or request methods:

HTTP Verb Action
GET Retrieves a representation of a resource without side-effects (nothing changes on the server).
POST Creates a resource.
PUT Replaces (completely) an existing resource.
DELETE Deletes an existing resource.

Url Format

The URL is specific for each operation but follows the same pattern for all the resources (only query string changes).

In case of an invalid URL the HTTP code 501 (not implemented) is returned to the invoker.

Actions: Complex operations (1)

A complex operation, which in theory should require the re-definition of new concepts mapped to new resources, can instead be implemented
in practice as an HTTP request that receives data from a resource (reference type TResource) and returns in the response an answer with specific information regarding, for example, a sub-resource of the resource sent in the request (reference type TResult).

Resource Content Manipulation

The resource data can be manipulated as a class instance or as plain text (type string) to avoid implementation of the resources following a class model.

Resource State Representation

The resource data can be sent or retrived always in the XML or JSON formats.

Error Handling

All error descriptons follows a well-defined schema – the ReSTException class – and basic HTTP status codes are used in order to allow the invoker Client to easily verify the success of the request.

To determine the success of the request the invoker must first verify the HTTP code returned in response headers, and in case of an error then load the response’s content that will contain an ErrorDescription structure in XML format.

Service Execution

The lifecycle of the implementation process of the HTTP request is the same for each resource, with the possible configuration of the initial (PreBeginRequest) and final (PreEndRequest) steps of each method on the resource: This enables the sharing by all or only some resources and / or methods of specific custom processes like the authentication step.

How to develop new web-services

The implementation of a new web-service implies a new custom class derived from a base and abstract class:

Service Description Must implement
CreateResourceMethod Base class for a new service to create new resources. The abstract method “Service_SaveResource” must be implemented (only) with the code to save the parameterized resource content.
ReadResourceMethod Base class for a new service to retrieve an existent resource. The abstract method “Service_LoadResource” must be implemented (only) with the code to load the resource with the parameterized identifier.
DeleteResourceMethod Base class for a new service to delete an existent resource. The abstract method “Service_DeleteResource” must be implemented (only) with the code to load the resource with the parameterized identifier.
EditResourceMethod Base class for a new service to update an existent resource. The abstract method “Service_SaveResource” must be implemented (only) with the code to save the resource with the parameterized resource content.
ListResourceMethod Base class for a new service to list existent resources. The abstract method “Service_LoadResources” must be implemented (only) with the code to retrieve the resources with the parameterized identifiers.
ActionResourceMethod Base class for a new service to perform some sort of action on a resource as described on (1). The abstract method “Service_Execute” must be implemented (only) with the code to process the request’s resource content and obtain the response’s (sub-)resource content.

 Rest Service Configuration

The configuration of the services and resources is done on the Global.asax file through the “RestConfiguration::addResourceMethod()” API method that allows to associate the new implemented service to a resource and method as well as plug-in the pre-begin and pre-end request operations.

Note

Serialization of resource classes is used through the use of the DataContractSerializer class.

Operations
Root URL

All REST services are accessed through the root URL.

The root URL is http:// hostname:port /rest

Create content (new resource)

The posted content is de-serialized according the request’s content type and then saved in the datasource.The current representation is returned in the HTTP request’s response.

Remarks

Only single resources can be created at one time.

In case of an internal error while saving the resource content on the overrided method then a custom “ReSTException” must be explicitly thrown.

How to do it
Verb POST
URL /{resource}[/?{query-string}]
Body The content of the resource to be created
RC 201 The resource was successfully created
RC 401 Not authorized to perform the action
RC 500 An internal error has occurred.
EXC ReSTAPI99901 The content of the resource was not sent.
EXC ReSTAPI41500 The format of the content of the resource is invalid.

Note:

  1. RC: HTTP return code
  2. EXC: Error code embbebed within a ReSTException XML structure

The request’s resource content can be either defined on XML or JSON format. The content type is obtained from the HTTP header “content-type” and expects  ”application/xml” or “application/json“.
The same format is used  to return the current state of the resource on the response.

How to use it
Example 1: Implementing resources as classes
// file addEmployeeService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class addEmployeeService : CreateResourceMethod<Employee>
    {
        protected override Int32 Service_SaveResource(Employee resource, NameValueCollection queryString)
        {
            //TODO: Code to save the resource in the datasource must be implemented here.

        }
    }
}
// file global.asax.cs

using Web.Services.Rest.API;

protected void Application_Start(object sender, EventArgs e)
{
            RestConfiguration.addResourceMethod<addEmployeeService,Employee>(
									RestConfiguration.Actions.CREATE,
									PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
Example 2: Using resource content directly as strings without use of intermediate classes
// file addEmployeeService.cs

namespace  Web.Services.Rest.Test
{
    public class addEmployeeService : CreateResourceMethod<string>
    {
        protected override Int32 Service_SaveResource(string resource, NameValueCollection queryString)
        {
            //TODO: Code to save the resource in the datasource must be implemented here.
        }
    }
}
// file global.asax.cs

namespace Web.Services.Rest.Test
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            RestConfiguration.addResourceMethod<addEmployeeService>(
                                    RestConfiguration.Actions.CREATE,
                                    "Employee",
                                    PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
        }
<!--Request (raw)-->

POST http://localhost:53489/rest/employee HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/xml;charset=utf-8
Content-Length: 164
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<?xml version="1.0" encoding="utf-8"?>
<Employee xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="www.training.com">
<Name>João Seixas</Name>
</Employee>

<!-- Response (raw) -->

HTTP/1.1 201 Created
Server: ASP.NET Development Server/10.0.0.0
Date: Sat, 22 Sep 2012 16:09:10 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml;charset=utf-8
Content-Length: 147
Connection: Close

<Employee xmlns:d1p1="www.training.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <d1p1:Name>João Seixas</d1p1:Name>
</Employee>
Update content (update existent resource)

The posted content is de-serialized according the request’s content type and then saved in the datasource. The current representation is returned in the HTTP request’s response.

An internal verification if performed in order to guarantee that an instance of the resource with the parameterized identifier exists.

Remarks

In case of an internal error while saving the resource content on the overrided method then a custom “ReSTException” must be thrown.

How to do it
Verb POST
URL /{resource}/{ID}[/?{query-string}]
Body The content of the resource to be replaced
RC 200 The resource was successfully updated
RC 401 Not authorized to perform the action
RC 500 An internal error has occurred.
EXC ReSTAPI99901 The content of the resource was not sent.
EXC ReSTAPI41500 The format of the content of the resource is invalid.

The request’s resource content can be either defined on XML or JSON format. The content type is obtained from the HTTP header “content-type” and expects “application/xml” or “application/json“.

How to use it
// file updateEmployeeService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class updateEmployeeService : EditResourceMethod<Employee>
    {
        protected override Int32 Service_SaveResource(Employee resource, NameValueCollection queryString)
        {
            //TODO: Code to save the resource in the datasource must be implemented here.

        }
    }
}
// file global.asax.cs

using Web.Services.Rest.API;

protected void Application_Start(object sender, EventArgs e)
{

            RestConfiguration.addResourceMethod<EditEmployeeService,Employee>(RestConfiguration.Actions.UPDATE, PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
<!--Request (raw)-->

POST http://localhost:53489/rest/employee/1 HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/xml;charset=utf-8
Content-Length: 172
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<?xml version="1.0" encoding="utf-8"?>
<Employee xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="www.training.com">
<Name>João Seixas updated</Name>
</Employee>

<!-- Response (raw) -->

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 23 Sep 2012 15:03:44 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml;charset=utf-8
Content-Length: 155
Connection: Close

<Employee xmlns:d1p1="www.training.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <d1p1:Name>João Seixas updated</d1p1:Name>
</Employee>
Delete content (delete existent resource)

The indicated resource identifier is used to identify the resource instance to remove. The result of the operation is returned in the HTTP request’s response.

An internal verification if performed in order to guarantee that an instance of the resource with the parameterized identifier exists.

Remarks

In case of an internal error while deleting the resource on the overrided method then a custom “ReSTException” must be explicitly thrown.

How to do it
Verb DELETE
URL /{resource}/{ID}
Body None
RC 200 The resource was successfully removed
RC 401 Not authorized to perform the action
RC 404 The resource does not exist
RC 500 An internal error has occurred.
How to use it
// file deleteEmployeeService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class deleteEmployeeService : DeleteResourceMethod<Employee>
    {
        protected override void Service_DeleteResource(long resourceID, NameValueCollection queryString)
        {
            //TODO: Code to delete the resource on the datasource must be implemented here.

        }
    }
}
// file global.asax.cs

using Web.Services.Rest.API;

protected void Application_Start(object sender, EventArgs e)
{

            RestConfiguration.addResourceMethod<deletemployeeService,Employee>(RestConfiguration.Actions.DELETE, PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
<!--Request (raw)-->

DELETE http://localhost:53489/rest/employee/1 HTTP/1.1
Accept-Encoding: gzip,deflate
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<!-- Response (raw) -->

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 23 Sep 2012 15:07:17 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Length: 0
Connection: Close
Get content (load existent resource)

The indicated resource identifier is used to identify the instance of the resource to load. The content of the loaded resource is serialized according the format indicated in the URL and sent as result within the HTTP request’s response.

Remarks

In case of an internal error while loading the resource then a custom “ReSTException” must be throwned.

If the resource is not found then the overrided method must return a NULL value.

If the output format is not specified in the URL then the XML is considered by default.

The iso code , when indicated in the URL, is identified an parameterized on the overrided method to be used as required. If no specific iso code is indicated then it is parameterized with the string “all”

How to do it
Verb GET
URL /{resource}/{ID}/[{ISO-CODE}]/[{FORMAT}] [/?{query-string}]
Body None
RC 200 The resource was successfully retrieved
RC 401 Not authorized to perform the action
RC 406 The output format specified in the URL is invalid
RC 500 An internal error has occurred.
EXC ReSTAPI40400 The indicated resource does not exist.
EXC ReSTAPI40600 The indicated output format is invalid.
How to use it
// file getEmployeeService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class getEmployeeService : ReadResourceMethod<Employee>
    {
        protected override Employee Service_LoadResource(long resourceID, string isoCode, NameValueCollection querystring)
        {
            //TODO: Code to read the resource on the datasource must be implemented here.

        }
    }
}
// file global.asax.cs

using Web.Services.Rest.API;

protected void Application_Start(object sender, EventArgs e)
{

            RestConfiguration.addResourceMethod<deletemployeeService,Employee>(RestConfiguration.Actions.READ, PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
<!--Request (raw)-->

GET http://localhost:53489/rest/employee/2/xml HTTP/1.1
Accept-Encoding: gzip,deflate
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<!-- Response (raw) -->

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 23 Sep 2012 15:16:47 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 147
Connection: Close

<Employee xmlns:d1p1="www.training.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <d1p1:Name>João Seixas</d1p1:Name>
</Employee>
Load list of contents (load list of resources)

The indicated page number is used to identify the instances of the resource to load. The loaded instances are serialized according the format indicated in the URL and sent as result within the HTTP request’s response.

The root node will have the name of the resource concatenated with the string “-list” (e.g: ). The collection of resources will be returned within the node

Remarks

In case of an internal error while loading the resources content on the overrided method then a custom “ReSTException” must be thrown.

If no resources are found then the overrided method must return a NULL value.

If the output format is not specified in the URL then the XML is considered by default.

The iso code , when indicated in the URL, is identified an parameterized on the overridable method to be used as required. If no specific iso code is indicated then it is parameterized with the string “all”.

How to do it
Verb GET
URL /{resource}/[{PAGENO-SIZE}]/[{ISO-CODE}]/[{FORMAT}] [/?{query-string}]
Body None
RC 200 The resources were successfully retrieved
RC 401 Not authorized to perform the action
RC 406 The output format specified in the URL is invalid
RC 500 An internal error has occurred.
EXC ReSTAPI40400 No resource was found.
EXC ReSTAPI40600 The indicated output format is invalid.
How to use it
// file listEmployeeService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class listEmployeeService : ListResourceMethod<Employee>
    {
        protected override List<Employee> Service_LoadResources(short pageNo, int pageSize, string isoCode, NameValueCollection queryString)
        {
            //TODO: Code to read the resources from the datasource must be implemented here.

        }
    }
}
// file global.asax.cs

using Web.Services.Rest.API;

protected void Application_Start(object sender, EventArgs e)
{

            RestConfiguration.listResourceMethod<deletemployeeService,Employee>(RestConfiguration.Actions.LIST, PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
<!--Request (raw)-->

GET http://localhost:53489/rest/employee/page/1-10/xml HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: null
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<!-- Response (raw) -->

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 23 Sep 2012 15:08:29 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 492
Connection: Close

<Employee-list xmlns:d1p1="http://schemas.datacontract.org/2004/07/Web.Services.Rest.API" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <d1p1:Items xmlns:d2p1="www.training.com">
    <d2p1:Employee>
      <d2p1:Name>João Seixas</d2p1:Name>
    </d2p1:Employee>
    <d2p1:Employee>
      <d2p1:Name>Ana Luísa</d2p1:Name>
    </d2p1:Employee>
  </d1p1:Items>
  <d1p1:NumberOfItems>2</d1p1:NumberOfItems>
  <d1p1:TotalItemsFound>2</d1p1:TotalItemsFound>
</Employee-list>
Complex actions on resources when HTTP verbs are not enough

The posted data and/or the information of the query string (1) is used to perform an operation over a resource (2) and produce result data (3) sent on the HTTP request’s response: The information in (1), (2), (3) is associated to co-related concepts (i.e. resources).

The input data, when sent, is first de-serialized according the request’s content type. The resource content and query string is processed in the overrided method. The output data is serialized according the format indicated on the initial request and returned in the HTTP request’s response.

The root node will have the name “root” and the contents of the otuput resource will be returned within it.

How to do it
Verb POST
URL /{resource}/{ACTION} [/?{query-string}]
Body The content of the resource OR empty string
RC 200 The request was successfully processed
RC 401 Not authorized to perform the action
RC 500 An internal error has occurred.
EXC ReSTAPI41500 The format of the content of the resource is invalid.

The request’s resource content can be either defined on XML or JSON format. The content type is obtained from the HTTP header “content-type” and expects  ”application/xml” or “application/json“.
The same format is used  to return the current state of the resource on the response.

How to use it
Example 1: Implementing resources as classes
// file userLoginService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class userLoginService : ActionResourceMethod<User,Session>
    {
        protected override void Service_Execute(User resource, out Session resource2, NameValueCollection queryString)
        {
            //TODO: Code to manipulate the resources must be implemented here.
            resource2 = new Session {ID = "44e7YGTF5434DD"};
        }
    }
}
// file global.asax.cs

protected void Application_Start(object sender, EventArgs e)
{
	RestConfiguration.addResourceMethod<userLoginService, User, Session>(
	                       "User", "Login", PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
Example 2: Using resource content directly as strings without use of intermediate classes
// file userLoginService.cs

using Web.Services.Rest.API;

namespace  Web.Services.Rest.Test
{
    public class userLoginService : ActionResourceMethod<User,string>
    {
        protected override void Service_Execute(User resource, out string resource2, NameValueCollection queryString)
        {
            //TODO: Code to manipulate the resources must be implemented here.
            resource2 = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?><root>session=44e7YGTF5434DD</root>";
        }
    }
}
// file global.asax.cs

protected void Application_Start(object sender, EventArgs e)
{
	RestConfiguration.addResourceMethod<userLoginService, User,string>(
	                         "User","Login",
	                         PlugInMethods.BeginRequest, PlugInMethods.EndRequest);
}
<!--Request (raw)-->

POST http://localhost:53489/rest/user/login/?username=jseixas&passwd=adm1n HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/xml;charset=utf-8
Content-Length: 191
Host: localhost:53489
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<!-- Response (raw) -->

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sat, 22 Sep 2012 17:59:31 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 137
Connection: Close

<root xmlns:d1p1="www.training.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <d1p1:ID>44e7YGTF5434DD</d1p1:ID>
</root>
Error Handling

Error reporting is global to all services: In case of identification of an error in the execution process the error description is always encapsulated in an object of type “ErrorDescription” and returned in XML format.

Remarks

If an error scenario is achived in any of the overrided methods then an exception of type “ReSTException” must be thrown.

How to do it
HTTP/1.1 415 Unsupported Media Type
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 23 Sep 2012 17:36:41 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 1404
Connection: Close

<Error xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Code>ReSTAPI41500</Code>
  <Message>Invalid format of the resource image / Formato da imagem do recurso é inválida</Message>
  <StackTrace>System.Runtime.Serialization.SerializationException: Erro na linha 2 posição 2. A esperar o elemento 'Employee' do espaço de nomes 'www.training.com'.. Encontrado 'Element' com o nome 'Employee1', espaço de nomes 'www.training.com'.
   em System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   em System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   em System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)
   em Web.Services.Rest.API.XmlSerializer.Deserialize[T](Byte[] image, String encoding) em C:\Users\João Seixas\Documents\TRAINING\projectos\Web.Services\Services.Rest.API\Serializer.cs:line 187
   em Web.Services.Rest.API.CreateResourceMethod`1.Service_Execute(HttpContext context, Serializer&amp; serializer, ResponseStatus&amp; status) em C:\Users\João Seixas\Documents\TRAINING\projectos\Web.Services\Services.Rest.API\CreateResourceMethod.cs:line 45</StackTrace>
  <Diagnostic>
	<Message>Diagnostic message #1</Message>
	<Message>Diagnostic message #2</Message>
	</Message>
  </Diagnostic>
</Error>
References

[1] The source code for the Web.Services.Rest.API assembly.