Showing posts with label proxy. Show all posts
Showing posts with label proxy. Show all posts

Friday, March 23, 2012

X-HTTP header method override and REST APIs





In a previous article, I was explaining how to extract HTTP headers. In this one, I will explain why the use of specific custom HTTP headers such as X-HTTP methods override can be very handy while developing and promoting a REST API.

When deploying REST API based web services, you may encounter access limitations on both the server and client sides.







1) X-HTTP methods override and server side

On the server side, the access to your REST based  web application (I am using JBOSS) might be done via a proxy web server that does not support certain HTTP operations such as DELETE or PUT for security reasons.

For example if you are using Lotus Domino as redirector for your HTTP requests, be aware that by default Lotus Domino does not enable PUT and DELETE to go through.

Such requests will lead to HTTP 405 error:

Http Status Code: 405

Reason: Request method is not allowed by the server

By the way, if you really intend to allow PUT and DELETE on your Domino proxy web server, you need to add the following line to your notes.ini file:

HTTPEnableMethods=PUT,DELETE

However if you really need to have your proxy server filtering DELETE and PUT operations, then you will have to offer these operations via POST operations and ask your clients to add specific X-HTTP method override field in the header of their requests.



2) X-HTTP methods override and client side

The application consuming your web service might also have some constraints, for example restricting forms to only use GET or POST.

For these reasons, web infrastructure and solutions providers have proposed to use customized HTTP header fields:
If you want to support all of these variations, I would suggest to implement them as case insensitive in your REST API.

I personally use JBoss RestEasy to develop a REST API for mobile health care applications, and it was quite straightforward to add the additional level of flexibility to my existing PUT and POST operations.

Note that if you want to use these methods to enforce either a PUT or a DELETE method, be aware that the RFC 2616 - HTTP 1.1 section 9.1.1 describes the GET operation as safe.

9.1.1 Safe Methods

   Implementors should be aware that the software represents the user in
   their interactions over the Internet, and should be careful to allow
   the user to be aware of any actions they might take which may have an
   unexpected significance to themselves or others.

   In particular, the convention has been established that the GET and
   HEAD methods SHOULD NOT have the significance of taking an action
   other than retrieval. These methods ought to be considered "safe".
   This allows user agents to represent other methods, such as POST, PUT
   and DELETE, in a special way, so that the user is made aware of the
   fact that a possibly unsafe action is being requested.


In other words, if you are tempted to use a GET to simulate a PUT or DELETE, don't do it.
Use a POST instead. Thank you to my colleague Ravi to point this to me!


X-HTTP methods override and PUT based operations

If your PUT operation does not have a corresponding DELETE counter-part,
you could just use the POST as a PUT and not even add a X-HHTP-Method override.

Both POST and PUT requests will end up being bound to the same method:


 @PUT
 @POST
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response putPostUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum)

If you still need to use a X-HTTP-Method override, what you need to do is to filter those specific POST requests.

boolean bProceed = true;
if (request.getMethod().compareToIgnoreCase(POST) == 0) {
    String methodOverride = getHttpMethodOverride(request);
    if ((methodOverride == null) || (methodOverride.compareToIgnoreCase(PUT) != 0)) 
        bProceed = false;
}

private String getHttpMethodOverride(HttpServletRequest request){
    String headerValue = request.getHeader("x-http-method-override");
    if (headerValue != null)
      return headerValue;
    else 
      return(request.getHeader("x-method-override"));  
}

In the example above, I have only implemented two customized X-HTTP headers, but you can add all of them if necessary.



X-HTTP methods override and DELETE based operations

For the DELETE operation, even if you don't have a PUT counter part, it is preferable to use a X-HTTP method override with DELETE to be semantically consistent (a PUT and POST are typically using to add or modify a resource, not removing it).

 
 @POST
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response postDeleteUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum)

boolean bDelete = false;
if (request.getMethod().compareToIgnoreCase(POST) == 0) {
 String methodOverride = getHttpMethodOverride(request);
 if ((methodOverride != null) && (methodOverride.compareToIgnoreCase(DELETE) == 0)) 
  bDelete = true;
}

Of course, if most likely want to still support the regular DELETE, so you also keep the original binding DELETE method:

 @DELETE
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response deleteUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum) 
 
 

Test harnessing X-HTTP Methods override operations


You can easily test your REST API with cURL as follow:

curl -H "X-HTTP-Method-Override: DELETE" -X POST http://restservice.acme.com/api/users/JJALGHPBIMA/orderNum/e7687673-479d-4d37-8bc0-a3e718aad33f

Should have the same effect as :

curl -X DELETE http://restservice.acme.com/api/users/JJALGHPBIMA/orderNum/e7687673-479d-4d37-8bc0-a3e718aad33f




Thursday, December 18, 2008

BlazeDS and secure Web Service access

In a previous post, I described how to use Flex/BlazeDS to a access remote Web Services.
This time, I am explaining how to access a secure Web Service that requires basic authentication using the same mechanism. This involves additional changes in the configuration files.

My goal is to create flex components that access ICW Lifesensor public Web services.
The additional complication is that the access to wsdl file required authentication over HTTPS.

Fortunately, I have the login and password of a Lifesensor Account (patient), so I can use them
to access the Reporting Web Services in order to retrieve medical data entries. More information about the Lifesensor APIs and Web services are available on the ICW Developer Network.

To illustrate my point, I will use a very simple Web Service call getVersion that return some general information about the web services such as the Axis version and build date (Lifesensor uses the open source Apache Axis framework to provide Web Services).

In the wsdl file for Version, the port description shows that the getVersion operation does not have any parameter, so the call to the Web Service will be straightforward:
<wsdl:porttype name="Version">
 <wsdl:operation name="getVersion">
  <wsdl:input message="impl:getVersionRequest" name="getVersionRequest"></wsdl:input>
  <wsdl:output message="impl:getVersionResponse" name="getVersionResponse"></wsdl:output>
  </wsdl:operation>
</wsdl:porttype>

BlazeDS offers a Proxy to access remote servers. This is necessary, if you do not have a crossdomain.xml file on your remote server. As a result, there will be two hops. One from the shockwave component on the client to the Proxy, the other one from the Proxy to the remote server where the Web Services reside.

For security reasons, both bops have to be secure, as a result, the initial SWF access and loading has to be done through HTTPS. In my case, my application (and the BlazeDS proxy) is served by Tomcat 6.0 that has been configured for SSL.

The first configuration change is in the proxy-config.xml. Besides the fact that the URL is now using HTTPS protocol, you will need also to specify the soap URL instead of using a wildchard in order to avoid a RPC Fault since "a destination that allows multiple domains or ports does not allow authentication".

Also, since LifeSensor is using basic access authentication, the easiest way to avoid the pop-up window from your browser asking you for the login and password (especially for the first hop, which is not relevant), is to set them in the proxy-config.xml initially via remote-user and remote-password tags.
     <destination id="ws-lifesensor-version">
       <properties>
           <wsdl>https://record2.us.lifesensor.com/phr/services/Version?wsdl</wsdl>
           <remote-username>?????</remote-username>
           <remote-password>?????</remote-password>
           <soap>https://record2.us.lifesensor.com/phr/services/Version</soap>
       </properties>
       <adapter ref="soap-proxy"/>
   </destination>

The MXML flex file is not very different from a Web Service access with no authentication. The following code is just specific to the Lifesensor Web Service API:
<mx:Script>
     <![CDATA[
       ...
       private function getData():void { webService_LS_Version.getVersion.send();}   
       ...
    ]]>
   </mx:Script>
   <mx:WebService id="webService_LS_Version" destination="ws-lifesensor-version" useProxy="true">
       <mx:operation name="getVersion"
               resultFormat="object"
               result="getData_result(event);"
               fault="getData_fault(event);">
       </mx:operation>
   </mx:WebService>

Calling the Version Web Service from LifeSensor (via an application on https://localhost:8443/) returns the following text:
"Apache Axis version: 1.4
Built on Apr 22, 2006 (06:55:48 PDT)"

In addition to this, my recommendations will be to use Flex remote debugging and a HTTP debugging proxy such as Charles or Fiddler which can be very handy to understand and debug AMF and SOAP based HTTP wrapped requests.

Also, Flex Builder 3.0 has very nice Web Service Introspection tool. Unfortunately, you will need to have a cross domain file on the server you want to introspect or have LifeCycle Data service to use the generated proxies. Apparently, if you only use BlazeDS, there are no direct ways to use the generated code out of the box. However, you can use some of the generated classes to store some of the data you obtain from the Web Services. This will be the topic of my next post.