Introduction
I know, the title seems misleading, but with
REST – Representational State Transfer, the simplicity comes with risk - if you
don’t understand and don't implement REST correctly. REST is not as simple as it
seems. The concept seems simple enough but the devils in the details as we will soon see. This
post will try to put forth some of the issues, with its implementation and their possible
solutions.
Firstly, there is so much written out
there on REST that is all a blur... viz.
REST is not a specification, it
is a theory.
The simplicity and power of the
whole concept.
REST is CRUD over HTTP and how
it is not!
GET
, POST
, PUT
, DELETE
verbs
make up REST. No SOAP, No RPC – development
simple and implementation even simpler. The whole debate over WCF REST
Services and Web API are not REST, they are RPC over HTTP GET
and POST
work over HTTP but
PUT
and DELETE
don’t.
Secondly, when you go and start the
implementation, there are many issues (server side and client side) which you could encounter.
This post will try to explain REST in
the simplest manner, with a very very basic implementation, and try to show
some of the problems with its practical implementations and will put forth some
ideas to solve those problems.
We
will demonstrate the following:
We will create a Generic Handler/IHTTPHandler
,
which will represent our REST Service. The HTTPHandler
will return us
a collection of books. We would be able to add, update, delete and query to
that collection over HTTP. The REST service will be using
Session
to store its data.
Note: REST
does not encourage Session
State management on the server, we are using it just
for this demo as an alternative to physical data store.
To test our REST Service we
will use, a HTTP Client - Postman - REST Client in the Chrome browser.
We will show how GET
and POST
requests work and how and why PUT
and DELETE
don’t and will show how to fix the
issues.
We
will not show the following:
We will not explain or discuss REST as a specification/practice in detail.We will not be creating a fancy
UI or a client to complicate the matters.
We are not using WCF REST
Service or ASP.NET Web API as we want to demonstrate the simplicity of the
concepts and issues surrounding the implementations.
Note: WCF REST Service or ASP.NET Web API use the HTTPHandler
as the root of it all.
The REST service is not connected to any backend or data source and we are not showing data access or that matter Entity Framework!
Implementation
We start by creating a new ASP .NET
Empty Web Application and name it RESTService-Demo1
.
To this newly created project, we add a new
Folder - Data. We add a class Book.cs and a Generic Handler BooksHandler.ashx to
the new folder.
Our Books.cs will be as follows:
Our Generic Handler BooksHandler.ashx –
will look as follows:
Some points of interest are highlighted
above and detailed below.
The Handler [BooksHandler]
implements IHttpHandler
,
which has a member ProcessRequest()
.
As we also need access to
Session
, we have implemented IRequiresSessionState
interface.
When the Handler
is called and
ProcessRequest()
method is executed, we can access the type of request, the
data sent by the client and much more via the context parameter. Based on the RequestType
, we
call corresponding methods in our REST service.
The GetBooks()
method is described
below:
We check if the client has passed in a
BookId
in the request, if it is not - then we construct the List
of Books
[5]
in a loop and store it in the session. We return the Books
list to the client in
JSON via the ReturnBooksToClient()
method.
If the user is interested in a
particular Book
, he or she passes its BookId
and we select the Book
"bookToReturn
” and return it to the client.
Our default GET
request and its response
will look as below. Here we haven’t passed in any BookId
and hence been returned
all the books in our list.
When we pass in the BookId
, we get the book
in response. Note here that the BookId
is passed in as a query string.
In our POST
request, we pass in a BookId
. Our
UpdateBook()
method,
looks for the BookId
in our Books
collection and updates it by appending “-
Updated” to it Name, Authors and Description data members. We then return all
the books in the collection as below...
In real world application, we would have to pass the data to be updated,
from our client. This is just a demo, so we are bending a few rules.
Now let us try to insert a new Book
to
our collection by passing in the PUT
request. Our handler already has all the
code to handle the request and update our books collection in its AddBook()
member.
As it turns out, we cannot pass a PUT
or even a DELETE
request to our Handler. We get the following error. "HTTP Error 405.0 - Method Not Allowed".
It is very clear from the error that the
RequestType
also called HTTP Verb or Method: PUT
and DELETE
we passed in to our handler
is not allowed.
Now the million dollar question is: who
is not allowing these verbs and why. If that is the case, then how is our REST
service aka Generic Handler going to work and how does it work elsewhere?
And the million dollar answer is: Our web application hosted on IIS is not
configured to handle PUT
and DELETE
requests. Typically GET
and POST
requests
are enough for communication over HTTP. In fact, ASP.NET uses POST
most exclusively.
Postback rings any bell?
PUT
and DELETE
are in a way considered
unsafe. Please note, I don’t want to sound alarmist but, there is solid thought
behind not allowing PUT
and DELETE
. PUT
is used for creating and DELETE
is
used for deleting obviously – Not allowing the user of the application access
to these verbs, means the user cannot create or delete anything directly. That
is why these two verbs are not allowed by default.
But REST wants to use these verbs! Hence we will
make sure our REST service gets what it demands.
Solution to the PUT and DELETE Predicament
Option 1: The configuration...
Following are the configurations for
allowing
PUT
and
DELETE
operation in our application.
For allowing CRUD [PUT
, GET
, POST
, and
DELETE
] operations over HTTP for our Handler aka REST Service, we need the
following settings:
The application needs to be hosted on an
IIS Web Server.
Need the Application Pool with:
.NET
Version Framework: 4.0
Pipeline
Mode: Integrated
Note: I am currently hosing with .NET
runtime 4.0, but configurations will almost be the same baring the runtime for
older .NET versions.
The required settings in the web.config are:
Important setting are highlighted. More
details can be found
here.
After the changes to our application on
IIS, the
PUT
and
DELETE
start working.
Option 2: The code...
There might be cases (some listed below), where we are not able to configure our applications with the above solution.
- Our System or Web Server Administrator is not allowing our application to have
PUT
or DELETE
permissions.
- We need to maintain the CRUD implementation in your service (
GetBooks()
, AddBook()
, DeleteBook()
), but give your clients a formal alternative for making PUT
or DELETE
requests.
Hence, a code solution to the problem. The technique is formally called as
Verb Tunneling - here we pass our
PUT
or
DELETE
requests bundled inside a
POST
request with the header "
X-HTTP-Method-Override
".
We won’t need any special configurations either on the IIS side or in our web.config file. We will use the code from our previous example as our base and build on top of it. The code will be available as RESTService-Demo2
.
We begin, by removing the handler configuration we added to our web.config file, in the above example. Note: We can maintain the original configuration in the web.config, if we wish to support both, the direct PUT
and DELETE
requests as well as the Verb Tunneling solution.
If updated, our web.config file will be very simple and will look like the following:
="1.0"
<configuration>
<system.web>
<compilation targetframework="4.5" debug="true">
<httpruntime targetframework="4.5">
</httpruntime>
</compilation>
</system.web>
</configuration>
Next we add a Global Application Class – Global.asax file to the root of the project. To the file, we add the following code to its Application_BeginRequest
event.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Request.RequestType.ToUpper() == "POST")
{
if (Request.Headers.AllKeys.Contains("X-HTTP-Method-Override", StringComparer.OrdinalIgnoreCase))
{
string overrideMethod = Request.Headers.Get("X-HTTP-Method-Override");
if (overrideMethod.Contains("PUT") || overrideMethod.Contains("DELETE"))
Request.RequestType = overrideMethod;
}
}
}
The Application_BeginRequest()
event is executed on each request - our application receives. In the event, we check if the Request is of type “POST
” and the Request Header contains an entry for "X-HTTP-Method-Override
". If it does, we convert the Request Type from POST
to the value passed in the "X-HTTP-Method-Override
" header.
We will continue from the earlier example and test our code with Postman - REST Client. Our GET
and POST
request/responses will remain the same, so we will focus on the PUT
and DELETE
requests.
Our updated request for DELETE
will look as follows:
Here, we are missing Book # 1 in the response, proving that our DELETE
has worked.
Our updated request for PUT(Insert)
will look as follows.
Here, we have gained Book # 6 in our response, proving PUT
also works.
These requests will reach our Application_BeginRequest()
event in our Application File(Global.asax) and will be transformed into respective PUT
and DELETE
request.
Here, our application, IIS configuration or our system administrators are not troubled as all the request come in as either GET
or POST
, which are considered SAFE.
Our clients, who are unable to make direct PUT
or DELETE
calls need to pass in their requests through the Request Header “X-HTTP-Method-Override
”.
For those clients who can make a PUT
or DELETE
request, there is no change, provided we have maintained our previous web.config file from our previous option.
This concludes our demo showing CRUD
over HTTP using Generic Handler used as a REST Service. Below are a few very
important points which need to he considered before opting for the techniques
described.
- The demo is only for
demonstration purposes, in real life application there would be more to our REST
Service like Authentication, Authorization and Validations.
- In most cases, your System
Administrator won’t allow you to have
DELETE
and PUT
permissions for your
applications. For instance, in our case, we are firing PUT
and DELETE
requests
on our application at will! In a real application, this would be unfathomable. This is not to say we cannot or should not do this,
but all the PROS and CONS need to be considered.
- There can be firewalls which would block
PUT
and DELETE
requests. - The clients need to be told, to send “
X-HTTP-Method-Override
” in the Request Header if the Verb Tunneling option is used for PUT
and DELETE
.
I hope now it would be clear why I said
“REST at your own risk!”
I will try to write something next time
which will have a client for our tiny REST service. Till then..
History
- March 4 2014: Original post
- May 01 2014: Added the "code option" for allowing
PUT
and DELETE
HTTP verbs