Epoxy is a REST/POX webservice API that extends the EntitySpaces Architecture. If you are unfamiliar with what REST/POX is I would recommend checking out the excellent series over at Softies On Rails. This addition is meant to empower the creation of massively public http API's for any application that you develop with the EntitySpaces Architecture. Why would you want to expose your EntitySpaces objects via REST/POX and not just provide the defacto SOAP service API? Well, lets face it not everyone is living in the .NET world. Don't you want all your cool ruby coding friends to be able to consume your services easily? Of course you do, not to mention the 100's of other geeks that will write clients if only you allow them to do it in the language of their choice without jumping through a lot of hoops. For a more authoritative answer on when to use REST/POX instead of or in conjunction with SOAP, check out what Don Box has to say about it.
So now that we all agree that this is indeed something very useful, lets get down to implementation. We chose to use the new WCF REST/POX facilities to create our services. Below is a sample of our Service Contract
[ServiceContract]public interface IUniversalContract{ [OperationContract(Action = "*", ReplyAction = "*")] Message ProcessMessage(Message input);}
As you can see our interface consists of one Operation Contract which simply takes a Message as an input. The implementation of the interface is where the rubber hits the road. Below is a sample implementation of the above defined interface using the Employees class generated from the Northwind sample database.
public Message ProcessMessage(Message request){ Message response = null; //The HTTP Method (e.g. GET) from the incoming HTTP request //can be found on the HttpRequestMessageProperty. The MessageProperty //is added by the HTTP Transport when the message is received. HttpRequestMessageProperty requestProperties = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; //Here we dispatch to different internal implementation methods //based on the incoming HTTP verb. if (requestProperties != null) { if (String.Equals("GET", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = GetEmployee(request); } else if (String.Equals("PUT", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = UpdateEmployee(request); } else if (String.Equals("POST", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = AddEmployee(request); } else if (String.Equals("DELETE", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = DeleteEmployee(request); } else { //This service doesn't implement handlers for other HTTP verbs (such as HEAD), so we //construct a response message and use the HttpResponseMessageProperty to //set the HTTP status code to 405 (Method Not Allowed) which indicates the client //used an HTTP verb not supported by the server. response = Message.CreateMessage(MessageVersion.None, String.Empty, String.Empty); HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty(); responseProperty.StatusCode = HttpStatusCode.MethodNotAllowed; response.Properties.Add( HttpResponseMessageProperty.Name, responseProperty ); } } else { throw new InvalidOperationException( "This service requires the HTTP transport" ); } return response;}
//The HTTP Method (e.g. GET) from the incoming HTTP request //can be found on the HttpRequestMessageProperty. The MessageProperty //is added by the HTTP Transport when the message is received. HttpRequestMessageProperty requestProperties = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
//Here we dispatch to different internal implementation methods //based on the incoming HTTP verb. if (requestProperties != null) { if (String.Equals("GET", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = GetEmployee(request); } else if (String.Equals("PUT", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = UpdateEmployee(request); } else if (String.Equals("POST", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = AddEmployee(request); } else if (String.Equals("DELETE", requestProperties.Method, StringComparison.OrdinalIgnoreCase)) { response = DeleteEmployee(request); } else { //This service doesn't implement handlers for other HTTP verbs (such as HEAD), so we //construct a response message and use the HttpResponseMessageProperty to //set the HTTP status code to 405 (Method Not Allowed) which indicates the client //used an HTTP verb not supported by the server. response = Message.CreateMessage(MessageVersion.None, String.Empty, String.Empty);
HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty(); responseProperty.StatusCode = HttpStatusCode.MethodNotAllowed;
response.Properties.Add( HttpResponseMessageProperty.Name, responseProperty ); } } else { throw new InvalidOperationException( "This service requires the HTTP transport" ); }
return response;}
As you can see the above maps various http verbs to our CRUD methods for our objects. Using the new EntitySpaces WCF support the WCF serialization worked like a charm.
Endpoints are very important in a REST'ful API, so its worth covering briefly how the WCF implementation handles this. For the context of this post my virtual directory locally is ServiceHost. The actual service file will be called employee.svc in this sample. This relates directly to the address you will use to interact with the API over HTTP.
**Note if you want this to work you must have WCF installed, be using EntitySpaces Developer 0614 or higher and configure IIS for WCF REST/POX operations.
In our sample to get a list of all employees you would hit
http://localhost/servicehost/employees.svc/employees/
this would produce the following XML
<ArrayOfanyURI xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <anyURI>http://scattorshot/servicehost/employees.svc/employees/1</anyURI> <anyURI>http://scattorshot/servicehost/employees.svc/employees/2</anyURI> <anyURI>http://scattorshot/servicehost/employees.svc/employees/3</anyURI></ArrayOfanyURI>
As you can see it provides you with a list or URI's for each employee in the list. You can also perform an HTTP POST to this address to add a new employee. You may retrieve a single employee with an EmployeeID of 1 by performing an HTTP GET to the below address
http://localhost/servicehost/employees.svc/employees/1
<Employees> <EmployeeID>1</EmployeeID> <LastName>Davolio</LastName> <FirstName>Nancy</FirstName> <Title>Sales Representative</Title> <TitleOfCourtesy>Ms.</TitleOfCourtesy> <BirthDate>1948-12-08T00:00:00</BirthDate> <HireDate>1992-05-01T00:00:00</HireDate> <Address>507 - 20th Ave. E. Apt. 2A</Address> <City>Seattle</City> <Region>WA</Region> <PostalCode>98122</PostalCode> <Country>USA</Country> <HomePhone>(206) 555-9857</HomePhone> <Extension>5467</Extension> <Photo> FRwvAAIAAn....the long base64 encoded string </Photo> <Notes> Education includes a BA in psychology from Colorado State University in 1970. She also completed "The Art of the Cold Call." Nancy is a member of Toastmasters International. </Notes> <ReportsTo>2</ReportsTo> <PhotoPath>http://accweb/emmployees/davolio.bmp</PhotoPath> <esRowState>Unchanged</esRowState></Employees>
After performing the HTTP GET and retrieving the desired employee resource you could modify it, and then perform an HTTP PUT to the same endpoint to update the employee. In order to delete an employee you would perform an HTTP DELETE to the above address to delete the resource. This is how your endpoints relate to your HTTP verbs, and is at the very heart of the REST style architecture.
So now you not only have the option to expose your EntitySpaces objects via SOAP services but REST/POX as well. We will be providing the full source code for the sample services as well as a small Windows Form client to consume the service. Choice is always a good thing, and this is certainly no exception.
From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases.EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.
The EntitySpaces Team--
EntitySpaces LLCPersistence Layer and Business Objects for Microsoft .NEThttp://www.entityspaces.net
Page rendered at Wednesday, March 17, 2010 2:41:44 AM (Eastern Standard Time, UTC-05:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.