Intro

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:

/wp-content/uploads/2014/02/chrome_error_399828.jpg

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:

Options

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.

To find out about order of execution of handler classes I would recommend the following blog by Christian Buckowitz: Debugging into a HTTP request on AS ABAP.

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:

/wp-content/uploads/2014/02/shmm_399849.jpg

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.

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

  1. Jeroen Custers

    Hi Alexander,

    Nice blog! I’m trying to implement what you did, but I’m still having issues related to authorization and CORS. I can’t access the gateway service. I get 401 unauthorized everytime. The only way it works is when I store the credentials in the SICF service or when I disable web security in chrome. It looks like the ZHANDLER doesn’t get called when there is no authorization.

    Thanks for your help!

    Best regards,

    Jeroen

    (0) 
    1. Alexander Sperling Post author

      Thanks Jeroen.

      As my scenario involved a pubilc service with user credentials of a technical user maintained I didn’t came across the problem mentioned by you. Hope you’re able to find a solution, would be great if you could post it in the comments here. I would update the blog then. If I find the time I’ll have a look myself but cannot promise anything at the moment.

      Regards,

      Alex

      (0) 
  2. Michael Van Geet

    Hi Alexander,

    Great blog, exactly what I need, but can’t get it to work 😉

    Method /IWBEP/IF_MGW_CONV_SRV_RUNTIME~SET_HEADER of the DPC_EXT class does not get called in standard, so redefinig it has little effect and there is no obvious place where we could call this method ourselves to add the header. Maybe I’m overlooking something. If not, how would you go about implementing it?

    Thx,

    Michael.

    (0) 
  3. Julien Caran

    You can set the header by redefining the method READ_ENTITY.

    Julien

    method /IWBEP/IF_MGW_CORE_SRV_RUNTIME~READ_ENTITYSET.
    
    CALL METHOD SUPER->/IWBEP/IF_MGW_CORE_SRV_RUNTIME~READ_ENTITYSET
      EXPORTING
        IV_ENTITY_NAME           = IV_ENTITY_NAME
        IV_SOURCE_NAME           = IV_SOURCE_NAME
        IS_PAGING                = IS_PAGING
        IT_ORDER                 = IT_ORDER
        IT_FILTER_SELECT_OPTIONS = IT_FILTER_SELECT_OPTIONS
        IS_REQUEST_DETAILS       = IS_REQUEST_DETAILS
      CHANGING
        CT_HEADERS               = CT_HEADERS
        CR_ENTITYSET             = CR_ENTITYSET
        cr_deleted_entityset     = cr_deleted_entityset
        cs_response_context      = cs_response_context
        ct_inline_info           = ct_inline_info.
    
      DATA ls_header  TYPE ihttpnvp.
      ls_header-name = 'Access-Control-Allow-Origin'.
      ls_header-value = '*.yourdomain.com'.
      APPEND ls_header TO ct_headers.
    
    
    endmethod.
    
    
    (0) 
  4. Erick Alberto Romero Neyra

    Hi Alexander

    We have the following scenario:

    • Design Studio + SDK  Component (Post Response Parser – with Ajax)
    • This SDK component allowed us to call ABAP web Service that resides in ICF
    • This is working fine in Internet Explorer (disabling SOP) but fails in Chrome and Firefox.

    With help of ABAP Developer we implemented CORS adding a class in the HTTP handler of /default_host/sap/opu/odata

    • ZJSONP_HANDLER (before standard class /IWBEP/IF_MGW_CONV_SRV_RUNTIME~SET_HEADER)

    HTTP_HANDLER.png


    Within ZJSONP_HANDLER class, we added the following:

    data: ls_header type ihttpnvp.

    ls_headername = ‘Access-Control-Allow-Origin’.

    ls_headervalue = ‘*’.

    /iwbep/if_mgw_conv_srv_runtime~set_header( ls_header ).

    ls_headername = ‘Access-Control-Allow-Methods’ .

    ls_headervalue = ‘POST, GET, OPTIONS, PUT, DELETE, HEAD’ .

    /iwbep/if_mgw_conv_srv_runtime~set_header( ls_header ).

    ls_headername = ‘Access-Control-Allow-Credentials’ .

    ls_headervalue = ‘true’ .

    /iwbep/if_mgw_conv_srv_runtime~set_header( ls_header ).

    ls_headername = ‘Access-Control-Allow-Headers’ .

    ls_headervalue = ‘Content-Type, Authorization, , x-csrf-token’ .

    /iwbep/if_mgw_conv_srv_runtime~set_header( ls_header ).

    ABAP developer also had to declare something to avoid an error message.

    But finally, after this we run the report and traced the flow in ABAP and code seems to flow ok.


    Anyway we’re getting this in chrome:

    Chrome_405_methodnotallowed.png

    chrome console error.png


    Looks like the headers are exposed but we still have this preflight (OPTIONS) when we send a GET or POST request.

    Could you give some advice on how to solve the preflight from server side (with ABAP on ICF)?

    Best Regards

    Erick

    (0) 

Leave a Reply