After all the theoratical introductions at recent SAP TechEd Events and some smaller projects in a local sandbox system I was finally getting to develop my own first Gateway based ODATA service that should be consumed by some website running jQuery. Setting up our little service, creating some entities, etc. was nothing really special after all the introductions and the excellent learning material in SCN.
Real world scenario issues
Of we go, our website development colleagues have included the provided service URI and are trying to read data from our service. Nothing happens within the jQuery script compared to directly calling the service in a browser where we get a nice response. What happened?
Looking into Chrome’s excellent developer tools we get a pointer to our issue:
We’re continuing our journey with tools from Google and finally end up getting really useful inside on ‘Cross Origin Ressource Sharing’. I recommend to have a look at the following:
- Solving “Same Origin Policy” Issue in different ways by Michael Herzog
Looking at the blog post from Michael Herzog I would have loved to use JSONP (as outlined in a custom handler in this post). Unfortunately (to my knowledge) NetWeaver Gateway is currently not supporting JSONP and I didn’t wanted to give up the complete Gateway framework and write a handler from scratch as outlined in mentioned blog from Alessandro Spadoni.
Setting up a proxy on the server providing the website would have been an option although I wanted to avoid this for two reasons: additional server load and being able to solve this problem on my own within Gateway.
So, next option would be sending an appropriate ‘Access-Control-Allow-Origin’ header in the response. I was unable to find any Gateway specific documentation regarding customizing so it seemed we need to take care of this on our own.
Adding the http header
The first and most appealing option of course would be to add the header directly in our service extension. And the good news is that one is actually able to do this. Right within your ‘DPC_EXT’ class you get access to method /IWBEP/IF_MGW_CONV_SRV_RUNTIME~SET_HEADER which can be used to set additional header fields. Nice and our first solution is working already.
Extending the solution
Now that we have the first solution in place I wanted to extend this a little bit in order to not send ‘*’ as the allowed requester and with this enabling everyone who is able to call my server to execute this service within his website. In other words I wanted to check the requester against a small customer table which would whitelist potential consumers of my service. However I was not able to get access to the needed http header field ‘Origin’ from the request to determine the requester.
Instead of using the service method of the entity I decided to write a small custom http handler which I could hook into SICF and which would check the requester and if ok add the header field. I won’t go into the details of writing the http handler class as such. This should be documented enough around here.
However when hooking the additional handler into SICF I stumbled over the following issues which I wanted to share to make life a little bit easier.
Order of execution of handler classes and flow of handlers
First of all our little handler class needs to be executed before the standard handler /IWFND/CL_SODATA_HTTP_HANDLER. Otherwise it will never be reached because after execution of the standard handler attribute if_http_extension~flow_rc will be set to 0 (or CO_FLOW_OK) which will tell the framework to not process any other handlers because request processing is complete. You can read more about this in SAP Help.
Second crucial point: the order of execution is top to bottom, in other words the handler maintained in node ‘odata’ will be executed first and only after that the handler in your actual SICF service down the line will be executed. Combining this with the knowledge about flow the handler on the service node will probably never be reached and in theory could be deleted. We will have to add our custom handler in node ‘odata’ in order to make it work.
So, finally we have everything in place, added our little handler into first place in node ‘odata’ but still nothing happens, a break-point in our custom handler code is not reached. What is going wrong? It took me a while to figure this one out. However by looking at function module ‘HTTP_GET_HANDLER_LIST’ it became clear that changes in SICF might not immediately be reflected during runtime as we have a parameter named ‘SHM_PARAM’. Shared memory seems to be involved and indeed, looking into transaction SHMM in our system one can easily spot an area which seems to used for ICM:
After having deleted this area our changes towards the handler list of node ‘odata’ had finally an impact on runtime processing and our little handler does his magic to check the request and add the header.
Final remarks and a word of caution
I would strongly suggest to keep in mind that you are adding your own logic towards the processing of requests inside of Gateway if you follow the example given inside this blog. This could have all kind of side effects towards processing, security, etc. so be careful.
Finally: I hope you did like the blog and hope that it invokes some discussion towards the route taken. It would be especially nice to get some information on whether something is in the pipeline for Gateway to solve this in SAP standard and whether Gateway will support JSONP in the future. Of course one could write their own JSONP handler extending Gateway. May be this is something for a follow-up blog.