Look, you don’t need to tell me.  I already know the truth, deep in my bones: ABAP is not the language the cool kids use.  Not even remotely on their radar.  They’ve got their Scalas, and their Rusts, and all 30 billion javascript frameworks to build hot startups with.  You can’t even play with ABAP unless you work for a place that runs SAP or you’re willing to download and install the massive (50 GB!) ABAP trial version

But when you look under the covers at the system functionality that ABAP exposes in SAP systems, it becomes apparent that the frameworks, libraries, and system tools that you have at your command can be crafted into an engine to power a phenomenal array of projects.  Just this morning I put together something that – while admittedly not incredibly useful all by itself – shows some of what you can accomplish if you have some familiarity with the tools SAP gives you in ABAP and Gateway. 

Let me show you.  It’s not a ridiculously complex build, and it’s just a slice of what you could do. 

At a high level what I’ve done is find an external RESTful web service, write some ABAP to consume it from the SAP side, and expose that data back out through Gateway.  It’s a little bit contrived, since you could easily call this service without Gateway mediating the connection…but I think there are occasional valid reasons to mediate the service through Gateway.  You might have an account with the external service and need to manage your calls to it, or you might want to join the service data with other things from Business Suite services and make it all available in one entity type.  Or you’re like me, and you just want to see if it could be done.  🙂

I created a developer account with world weather online, so that I could use its API for free.  This lets you use a simple call to get a set of information on weather for a particular location, and in my case I use it to get the day’s forecast with a given zip code.  If you sign up, you can use their neat API explorer to test out different ways to use the service. 

If I call the main weather service with my home zip code, I get the following structure back (some unnecessary stuff has been trimmed):

<?xml version=“1.0” encoding=“UTF-8”?>
<data>
   
<request>
       
<type>Zipcode</type>
       
<query>55426</query>
   
</request>
   
<weather>
       
<date>2014-03-27</date>
       
<tempMaxC>5</tempMaxC>
       
<tempMaxF>40</tempMaxF>
       
<tempMinC>-6</tempMinC>
       
<tempMinF>22</tempMinF>
       
<windspeedMiles>15</windspeedMiles>
       
<windspeedKmph>24</windspeedKmph>
       
<winddirection>ESE</winddirection>
       
<winddir16Point>ESE</winddir16Point>
       
<winddirDegree>123</winddirDegree>
        <weatherDesc>
            <![CDATA[Light rain]]>
       
</weatherDesc>
       
<precipMM>9.6</precipMM>
   
</weather>
</data>


Knowing the structure of what comes back to me, I can build some simple ABAP to do the same thing.  I set up a dictionary z-structure to hold the bits of data that I want to use:

weather structure.PNG


I then set up a function module to do pull data from the service and put it into that structure:

FUNCTION zweather_read_zip.
*”—————————————————————
*”*”Local Interface:
*”  IMPORTING
*”     VALUE(IM_ZIPCODE) TYPE  AD_PSTCD1
*”  TABLES
*”      ET_WEATHER STRUCTURE  ZWEATHER
*”—————————————————————

  DATA: lo_http_client TYPE REF TO if_http_client,
        lv_service TYPE string,
        lv_result TYPE string,
        lo_ixml TYPE REF TO if_ixml,
        lo_streamfactory TYPE REF TO if_ixml_stream_factory,
        lo_istream TYPE REF TO if_ixml_istream,
        lo_document TYPE REF TO if_ixml_document,
        lo_parser TYPE REF TO if_ixml_parser,
        lo_weather_element TYPE REF TO if_ixml_element,
        lo_weather_nodes TYPE REF TO if_ixml_node_list,
        lo_curr_node TYPE REF TO if_ixml_node,
        lv_value TYPE string,
        lv_node_length TYPE i,
        lv_node_index TYPE i,
        ls_weather TYPE zweather,
        lv_node_name TYPE string,
        lv_node_value TYPE string.

  lv_service = http://api.worldweatheronline.com/free/v1/weather.ashx.

  lv_service = lv_service && ‘?q=’ && im_zipcode && ‘&format=xml’.

  lv_service = lv_service && ‘&key=[use your own!]’.

  cl_http_client=>create_by_url(
    EXPORTING
      url                = lv_service
    IMPORTING
      client             = lo_http_client
    EXCEPTIONS
      argument_not_found = 1
      plugin_not_active  = 2
      internal_error     = 3
      OTHERS             = 4 ).

  lo_http_client->send(
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2 ).

  lo_http_client->receive(
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3 ).

  “Prepare XML structure
  CLEAR lv_result .
  lv_result = lo_http_client->response->get_cdata( ).
  lo_ixml = cl_ixml=>create( ).
  lo_streamfactory = lo_ixml->create_stream_factory( ).
  lo_istream = lo_streamfactory->create_istream_string(
                                   lv_result ).
  lo_document = lo_ixml->create_document( ).
  lo_parser = lo_ixml->create_parser(
                         stream_factory = lo_streamfactory
                         istream        = lo_istream
                         document       = lo_document ).

  “This actually makes the XML document navigable
  lo_parser->parse( ).

  “Navigate XML to nodes we want to process
  lo_weather_element = lo_document->find_from_name_ns( ‘weather’ ).
  lo_weather_nodes = lo_weather_element->get_children( ).

  “Move through the nodes and assign appropriate values to export
  lv_node_length = lo_weather_nodes->get_length( ).
  lv_node_index = 0.
  CLEAR ls_weather.
  WHILE lv_node_index < lv_node_length.
    lo_curr_node = lo_weather_nodes->get_item( lv_node_index ).
    lv_node_name = lo_curr_node->get_name( ).
    lv_node_value = lo_curr_node->get_value( ).

    CASE lv_node_name.
      WHEN ‘date’.
        REPLACE ALL OCCURRENCES OF ‘-‘ IN lv_node_value WITH .
        ls_weatherforecast_date = lv_node_value.
      WHEN ‘tempMaxF’.
        ls_weatherhigh_temp_f = lv_node_value.
      WHEN ‘tempMinF’.
        ls_weatherlow_temp_f = lv_node_value.
      WHEN ‘windspeedMiles’.
        ls_weatherwind_speed = lv_node_value.
      WHEN ‘winddir16Point’.
        ls_weatherwind_direction = lv_node_value.
      WHEN ‘weatherDesc’.
        ls_weatherdescription = lv_node_value.
      WHEN ‘precipMM’.
        ls_weatherprecipitation = lv_node_value.
    ENDCASE.

    ADD 1 TO lv_node_index.
  ENDWHILE.

  APPEND ls_weather TO et_weather.

ENDFUNCTION.


I sprinkled some comments in the code to help, but I use the cl_http_client class to do the call to the service (and it can be set up and done in just 3 method calls), and then use a few of the xml library classes to parse the result and put it into the structure.  You can see here that though I’ve only made the zip code dynamic in the call, you could actually be pretty dynamic in choosing services to leverage. 

Why did I use a function module?  It’s actually pretty easy to use a function module as a basis for building services in SEGW on your Gateway system, so it can be convenient to wrap custom functionality into a function module and then just import definitions.  That’s what I did to set up the entity and entity set for this simple service: 

weather demo segw setup.PNG

Note especially that the GetEntity method is mapped with zip code as the incoming parameter:

weather demo get entity.PNG


After this, when I activated the service, it’s as simple as calling the URI with a parameter of some zip code that I want to see :


weather service result.PNG


Like I mentioned before, by itself this isn’t much use.  But combine this with some other information and you start to see what you can really pull together and expose through Gateway.  It’s pretty awesome to think that anything you could pull from a REST service on the web can also be a resource for your application. 

To report this post you need to login first.

70 Comments

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

  1. Dhruv Kashyap

    Hi Paul,

    Thanks for the post. I am trying to create the function module but somehow I get an error in the following statements.

      lv_service = lv_service && ‘?q=’ && im_zipcode && ‘&format=xml’.

      lv_service = lv_service && ‘&key=[use your own!]’.


    ABAP is not able to recognize “&&” as an operator.


    Also in the next statement what do you mean by “use your own”.

    Thanks for your time.


    Regards,

    Dhruv

    (0) 
    1. Paul J. Modderman Post author

      Hi Dhruv,

      ‘&&’ is a shorthand for “CONCATENATE” that is available on the newest NetWeaver ABAP, so I think you might be on 7.31 or earlier.  Sorry about that.  The corresponding CONCATENATE statement would be something like:

      CONCATENATE lv_service ‘q=’ im_zipcode ‘&format=xml&key=[use your own!]’ INTO lv_service.

      The [use your own!] represents the API key that you get from http://api.worldweatheronline.com/ when you register as a (free) developer on their site.  I just didn’t want to put my API key on the blog posting. 

      Hope that helps, please let me know if it doesn’t!

      Paul

      (0) 
  2. Ashwin Dutt R

    Hello Paul,

    Thanks for sharing . I tried it and worked as expected 🙂

    How do we pass our Payload to the method ‘  create_by_url ‘ when we have Create , Delete & Update operations ? & also Payload if we want to operate on BATCH mode for any of our CRUD operations ?

    I know how to pass Payload to GW for any CRUD operations ( BATCH as well ).

    Just want to know how actually we can pass to the method ‘ create_by_url ‘ 🙂

    It would be great if you can share the information.

    Regards,

    Ashwin

    (0) 
    1. Paul J. Modderman Post author

      Hi Ashwin,

      I haven’t yet done anything to push CUD of CRUD to an external REST service from SAP.  But looking through the API structure of CL_HTTP_CLIENT and CL_HTTP_REQUEST, I believe you could do it.

      Using the code above as a starting point, right after the call to create_by_url you have an object lo_http_client of type cl_http_client.  This object has a publicly accessible attribute called “request”, which is an object of type cl_http_request.  The “request” object has support for changing the HTTP method (by calling method “set_method”)  and appending character data to the body (by calling method “append_cdata” or “append_cdata2”).

      It would look something like this.  Inserted after the call the “create_by_url”.

      DATA: lo_http_request TYPE REF TO cl_http_request. 

      lo_http_request = lo_http_client->request. 

      lo_http_request->set_method( ‘POST’ ).

      lo_http_request->append_cdata( data = [your request body] ).

      Then I believe you could continue on with the execution of the http_client calls, as the client now has the request object set up correctly for other REST activities.

      Thanks,

      Paul

      (0) 
      1. Vishnu Kiran Reddy Chintala

        Hi Paul,

        I have tried as per your suggestion and below is the piece of code.

        “Payload content

          lv_post =

           ‘{ “preOptionCodes”: [ {“code”: “7012”, “priorityCode”: true} ],”currentOptionCode”: {“code”: “0202”, “priorityCode”: true},”baseCode”: “01J4H” }’.


        “set http method POST

           CALL METHOD lo_http_client->request->set_method( if_http_request=>co_request_method_post ).


        “set protocol version                                 

           CALL METHOD lo_http_client->request->set_version(

             if_http_request=>co_protocol_version_1_1 ).


        “content type

           CALL METHOD lo_http_client->request->if_http_entity~set_content_type

             EXPORTING

               content_type = ‘application/json’.


        “Set Header field

           CALL METHOD lo_http_client->request->set_header_field

             EXPORTING

               name  = ‘Accept’

               value = ‘*/*’.


        CALL METHOD lo_http_client->request->set_header_field

             EXPORTING

               name  = ‘Accept-Encoding’

               value = ‘gzip,deflate,sdch’.


        CALL METHOD lo_http_client->request->set_header_field

             EXPORTING

               name  = ‘Accept-Language’

               value = ‘en-US,en;q=0.8’.


        CALL METHOD lo_http_client->request->append_cdata2( data = lv_post ).

           ” Formfield encoding

           CALL METHOD lo_http_client->request->if_http_entity~set_formfield_encoding

             EXPORTING

               formfield_encoding = cl_http_request=>if_http_entity~co_encoding_raw.

        I am getting below error.

        Application Error
        Error: System failure while executing your request.
        Message: Could not read JSON: Invalid numeric value: Missing integer part (next char ‘-‘ (code 45)) at [Source: com.ibm.ws.webcontainer.srt.http.HttpInputStream@3f87eaf5; line: 1, column: 3]; nested exception is org.codehaus.jackson.JsonParseException: Invalid numeric value: Missing integer part (next char ‘-‘ (code 45)) at [Source: com.ibm.ws.webcontainer.srt.http.HttpInputStream@3f87eaf5; line: 1, column: 3]
          Please Contact Support team for more information.

        Could you please suggest me, where i did the mistake?

        Thanks,

        Vishnu

        (0) 
        1. Paul J. Modderman Post author

          On first glance of the message you’re getting, it seems like the problem is on the WebSphere side, deserializing the JSON.  I don’t know a whole lot about that side of the equation…is there something you need to do to tell it that the values you’re sending should be considered strings and not numbers?

          I’m assuming because you have codes like “0202” and “01J4H” that you’re not trying to send any numeric values across. 

          (0) 
          1. Vishnu Kiran Reddy Chintala

            Hi Paul,

            Thanks for your reply. I have used method SET_CDATA rather than APPEND_CDATA and it is working fine.

            ****JSON String to POST

              lv_post =

              ‘{“preOptionCodes”:[{“code”:”7012″,”priorityCode”:true}],”currentOptionCode”:{“code”:”0202″,”priorityCode”:true},”baseCode”:”01J4H”}’.

            ****content type

              CALL METHOD lo_http_client->request->if_http_entity~set_content_type

                EXPORTING

                  content_type = ‘application/json; charset=utf-8’.”‘application/json’.

            ****Set Header field

              CALL METHOD lo_http_client->request->set_header_field

                EXPORTING

                  name  = ‘Accept’

                  value = ‘*/*’.

            *

              CALL METHOD lo_http_client->request->set_header_field

                EXPORTING

                  name  = ‘Accept-Encoding’

                  value = ‘gzip,deflate,sdch’.

            *

              CALL METHOD lo_http_client->request->set_header_field

                EXPORTING

                  name  = ‘Accept-Language’

                  value = ‘en-US,en;q=0.8’.

            *****Set the Data

              DATA:lt_data TYPE xstring,

                   lv_len  TYPE i.

              lv_len = strlen( lv_post ).

              CALL METHOD lo_http_client->request->set_cdata   ” Removed APPEND_CDATA

                EXPORTING

                  data   = lv_post

                  offset = 0

                  length = lv_len.

            ****Make the call

              lo_http_client->send(

                EXCEPTIONS

                  http_communication_failure = 1

                  http_invalid_state         = 2 ).

            ****Receive the Response Object

              lo_http_client->receive(

                EXCEPTIONS

                  http_communication_failure = 1

                  http_invalid_state         = 2

                  http_processing_failed     = 3 ).

            ***Get the response content in Character format

              CLEAR lv_result .

              lv_result = lo_http_client->response->get_data( ).

              WRITE:lv_result.

            Thanks,

            Vishnu

            (0) 
            1. Dharmin Doshi

              Hi Vishnu,

              I am doing the same thing posting json data using POST method to web API.

              I have followed similar approach but still it does not post the data.

              Can you kindly help here as I am unable to proceed.

              Thanks.

              (0) 
        2. raju m

          hi can you please help me how we can pass INPUT as XML data to REST service , my requirement is similar to above. url:   http://demo.mercurytravels.in/nextrapiv2.jsp

          <flightsearchrequest>

            <credentials>

            <username>apiXXXXXXXX</username>

            <password>apiXXXXXXX</password>

            <officeid>XXXXXXXX</officeid>

            </credentials>

            <origin>BLR</origin>

            <destination>BOM</destination>

            <onwarddate>2014-10-10</onwarddate>

            <returndate>2014-10-10</returndate>

            <numadults>1</numadults>

            <numchildren>1</numchildren>

            <numinfants>1</numinfants>

            <journeytype>OneWay</journeytype>

            <requestformat>JSON</requestformat>

            <preddeptimewindow />

            <prefarrtimewindow />

            <resultformat>XML</resultformat>

            <prefclass>E</prefclass>

            <prefcarrier>All</prefcarrier>

            <excludecarriers />

            <searchtype>Normal</searchtype>

            <promocode />

            <numresults>25</numresults>

            <sortkey>default</sortkey>

            <actionname>FLIGHTSEARCH</actionname>

            </flightsearchrequest>

          (0) 
    1. Paul J. Modderman Post author

      Your question could be taken two ways, hopefully I clear it up with how I answer.  🙂

      If you’re asking about testing the GW service URI, then you can use the transaction /IWFND/GW_CLIENT.  You won’t have to use any ABAP to do that. 

      But I think you’re asking about a scenario where you’d call an external GW system’s URI from your ABAP program?  In that case, you’d likely just have to change the full URI to include the external path to the GW system you’re wanting to call. 

      Maybe you can explain more on what you’re asking about? 

      (0) 
  3. Greg Gipson

    Paul,

    Maybe i’m reading this incorrectly, i thought  this example was wrapping the function module with the GW GetEntity method and being utilized from ether an inside/out or outside/in perspective.

    I was thinking that in the former one could either call the function module directly or call the GW service directly from maybe a ABAP program.

    I’m questioning whether it’s possible to call the GW service from within SAP and if this technique could be used in a similiar fashion as to call a wrapped RFC on a external system.

    Sorry for the confusion, there’s not much out there about SAP consuming external resouces.

    Thanks,

    Greg

    (0) 
    1. Paul J. Modderman Post author

      Ah, ok.  Thanks for the clarification.  You’re right, this example was wrapping the FM with the GetEntity method. 

      I think (not having tried it personally yet) that trying to consume the GW service from within the same system in ABAP code would be a daunting task, because there’s a lot of stuff that happens at runtime and it would take a lot of work to just uncover what you need to have setup to make the call correctly.  I think you’d be better off calling the FM directly. 

      Paul

      (0) 
      1. Greg Gipson

        Hi Paul,

        How does this compare with using CL_REST_HTTP_CLIENT? Do you know of any examples?  I see you’re using a key provided by your registration to use the service, but any idea how this works with other various security mechanisms?

        Thanks,

        Greg

        (0) 
  4. Kevin Deuel

    Hi Paul,

    Thanks for this blog. I am not sure if you still check the comments here or not, but I have a question for you about configurations to get this working.

    Thanks!

    -Kevin

    (0) 
    1. Paul J. Modderman Post author

      I get comment alerts 🙂

      If you want to ask the question publicly, feel free and I’ll answer.  Otherwise I think we need to “follow” each other to be able to exchange private messages.

      Paul

      (0) 
      1. Kevin Deuel

        wow thanks for the quick response!

        Ok, kind of a newb question: Do we have to use Gateway to consume a REST Web Service like the weather service in an ABAP Function?

        (0) 
        1. Paul J. Modderman Post author

          As long as you have some way to call out to that external service (like I did through the cl_http_client class), then any ABAP system could consume a REST web service. 

          Hit me up with a private message if you want to go through an example or something.  I always find that stuff fun!

          (0) 
      1. Stephen Herlick

        Hi Paul

        The link above worked for me as well.  It doesn’t work when I use my key but if I change the v1 to v2 with my key it does work.  If you change the v1 to v2 in the link above, it fails with your key.

        I guess the version of the API is tied to the key.

        Stephen

        (0) 
  5. nainesh patadia

    Hi,

    I have Restful API URL. I have to call it in background to trigger SMS. I use the same code which u mention in you blog create_url and then send method. But its not sending SMS to my mobile. Do we need gateway settings into this case. Normally when running the same URL using call_browser its working fine but its in foreground. I want this to be done in background without any pop-up. Do we need and HTTP settings, If yes then give me detail steps.

    Thanks in advance. For this useful post also.

    Cheers,

    Nainesh Patadia

    (0) 
  6. Peter Simm

    Hi Paul,

    I followed your guide. Unfortunately I did anything
    wrong. When executing transaction /IWFND/SUTIL_GW_CLIENT to test the service, I
    get the following error message:

       

    Network Error (tcp_error)
    A communication error occurred: “Connection refused” 

    The Web Server for your query “http://sfc1sappmcid1.[xxxxx].com:8010/sap/opu/odata/sap/ZWEATHER_DEMO_SRV/DailyWeatherCollection('01109‘)
    may be down, too busy, or experiencing other problems preventing it from responding to requests. You may wish to try again at a later time. 

       

    It seems the system passes the SAP service to the internet not the URL. When calling the link

    http://api.worldweatheronline.com/free/v2/weather.ashx?q=&format=xml
    &key=[mykey]

    I get the expected weather data. Do you have any idea what I did wrong?

    Thank you, Peter

    (0) 
      1. nainesh patadia

        Hi Paul,

          Thanks for your quick reply.

        I am giving my code here.

        ” Local Vars

        DATA: lo_client  TYPE REF TO if_http_client,

               lc_url     TYPE string,

               lc_content TYPE string,

               lt_html    TYPE TABLE OF string.

        ” Set html page to load

        lc_url = ‘http://www.sap.com‘.

        ” Create the HTTP client

        CALL METHOD cl_http_client=>create_by_url

           EXPORTING

             url    = lc_url

           IMPORTING

             client = lo_client

           EXCEPTIONS

             OTHERS = 1.

        IF sy-subrc IS NOT INITIAL.

           ” Error

           EXIT.

        ENDIF.

        ” Set the Request type to GET

        lo_client->request->set_header_field( name  = ‘~request_method’

                                               value = ‘GET’ ).

        ” Make the call

        lo_client->send( ).

        ” Receive the Response Object

        CALL METHOD lo_client->receive

           EXCEPTIONS

             http_communication_failure = 1

             http_invalid_state         = 2

             http_processing_failed     = 3

             OTHERS                     = 4.

        IF sy-subrc IS NOT INITIAL.

           ” Error

           EXIT.

        ENDIF.

        I am using above code and getting connection refuse error.

        My requirement is to trigger Restful API which sends SMS to mobile.

        I have change URL from Restful SMS API to http://www.sap.com here in this post rest all is same.

        (0) 
        1. Paul J. Modderman Post author

          You should try program RSHTTP20 and put in the URI you’re trying to use there (I suggest using ‘http://www.sap.com/index.html‘ instead of sap.com, since sap.com redirects to the index page). 

          What that program will do is go through the HTTP request process and tell you what kind of response you’re getting, or it will tell you if there’s an error in your SAP_HTTP RFC setup. I suspect that that’s where the error is – I have a test system that isn’t configured to make outbound HTTP calls and I get the same error you’re getting, but when I use a system properly configured to make outbound HTTP calls I can get a success from the RECEIVE method. 

          Hope this helps. 

          (0) 
          1. nainesh patadia

            Thanks Paul,

            Can you let me know steps to check that SAP_HTTP Rfc is configured or not in my system?

            So, I can check and test it again.

            Thanks a ton for your quick reply.

            (0) 
  7. Peter Simm

    Hi Paul,

    thank you for your fast answer. I went through my setup and made some screenshots and copies from what I did for configuration.  Probably I did a stupid mistake but I am not able to identify it.

    Thank you,
    Peter

     

    1. Functionmodule ZWEATHER_READ_ZIP

    First I have created the FM ZWEATHER_READ_ZIP with the following coding:

     

    FUNCTION zweather_read_zip.
    *”————————————————————–
    *”*”Lokale Schnittstelle:
    *”  IMPORTING
    *”     VALUE(IM_ZIPCODE) TYPE  AD_PSTCD1
    *”  TABLES
    *”      ET_WEATHER STRUCTURE  ZWEATHER
    *”————————————————————-
     
    DATA: lo_http_client     TYPE REF TO if_http_client,
            lv_service        
    TYPE        string,
            lv_result         
    TYPE        string,
            lo_ixml           
    TYPE REF TO if_ixml,
            lo_streamfactory  
    TYPE REF TO if_ixml_stream_factory,
            lo_istream        
    TYPE REF TO if_ixml_istream,
            lo_document       
    TYPE REF TO if_ixml_document,
            lo_parser         
    TYPE REF TO if_ixml_parser,
            lo_weather_element
    TYPE REF TO if_ixml_element,
            lo_weather_nodes  
    TYPE REF TO if_ixml_node_list,
            lo_curr_node      
    TYPE REF TO if_ixml_node,
            lv_value          
    TYPE        string,
            lv_node_length    
    TYPE        i,
            lv_node_index     
    TYPE        i,
            ls_weather        
    TYPE        zweather,
            lv_node_name      
    TYPE        string,
            lv_node_value     
    TYPE        string.

      lv_service = http://api.worldweatheronline.com/free/v2/weather.ashx.
     
    lv_service = lv_service && ‘q=’ && im_zipcode && ‘&format=xml’.
      lv_service
    = lv_service && ‘&key=xxxxxxxxxxxxxxxxxxxxxxxxxxx’.

      cl_http_client=>create_by_url(
       
    EXPORTING
          url               
    = lv_service
       
    IMPORTING
         
    client             = lo_http_client
       
    EXCEPTIONS
          argument_not_found
    = 1
          plugin_not_active 
    = 2
          internal_error    
    = 3
         
    OTHERS             = 4 ).

      lo_http_client->send(
       
    EXPORTING
          timeout
    = 20
       
    EXCEPTIONS
          http_communication_failure
    = 1
          http_invalid_state
    = 2
          http_processing_failed
    = 3
          http_invalid_timeout
    = 4 ).

      lo_http_client->receive(
       
    EXCEPTIONS
          http_communication_failure
    = 1
          http_invalid_state        
    = 2
          http_processing_failed    
    = 3 ).

      “Prepare XML structure
     
    CLEAR lv_result .
      lv_result
    = lo_http_client->response->get_cdata( ).
     
    lo_ixml = cl_ixml=>create( ).
      lo_streamfactory
    = lo_ixml->create_stream_factory( ).
      lo_istream
    = lo_streamfactory->create_istream_string( lv_result ).
      lo_document
    = lo_ixml->create_document( ).
      lo_parser
    = lo_ixml->create_parser(
                             stream_factory
    = lo_streamfactory
                             istream       
    = lo_istream
                             document      
    = lo_document ).

      “This actually makes the XML document navigable
      lo_parser
    ->parse( ).

      “Navigate XML to nodes we want to process
      lo_weather_element
    = lo_document->find_from_name_ns( ‘weather’ ).
      lo_weather_nodes
    = lo_weather_element->get_children( ).

      “Move through the nodes and assign appropriate values to export
      lv_node_length
    = lo_weather_nodes->get_length( ).
      lv_node_index
    = 0.
     
    CLEAR ls_weather.
     
    WHILE lv_node_index < lv_node_length.
        lo_curr_node
    = lo_weather_nodes->get_item( lv_node_index ).
        lv_node_name
    = lo_curr_node->get_name( ).
        lv_node_value
    = lo_curr_node->get_value( ).

        CASE lv_node_name.
         
    WHEN ‘date’.
           
    REPLACE ALL OCCURRENCES OF ‘-‘ IN lv_node_value WITH .
            ls_weather
    forecast_date = lv_node_value.
         
    WHEN ‘tempMaxF’.
            ls_weather
    high_temp_f = lv_node_value.
         
    WHEN ‘tempMinF’.
            ls_weather
    low_temp_f = lv_node_value.
         
    WHEN ‘windspeedMiles’.
            ls_weather
    wind_speed = lv_node_value.
         
    WHEN ‘winddir16Point’.
            ls_weather
    wind_direction = lv_node_value.
         
    WHEN ‘weatherDesc’.
            ls_weather
    description = lv_node_value.
         
    WHEN ‘precipMM’.
            ls_weather
    precipitation = lv_node_value.
       
    ENDCASE.

        ADD 1 TO lv_node_index.
     
    ENDWHILE.

      APPEND ls_weather TO et_weather.

    ENDFUNCTION.

    2. Create project in SEGW

    SEGW.jpg

    3. I went to SPRO > SAP NetWeaver  > Gateway > OData > General Setting > Activate and Maintain Services (/IWFND/MAINT_SERVICE)

    Here I have activated the Service as shown in the screenshot:

    Activate and Maintain Service.JPG

    When done I have tested the service pressing the “Gateway Clienet” button in transaction /IWFND/MAINT_SERVICE. Here I passed the parameter DailyWeatherCollection(‘01109’) into the URI and pressed “execute”.

    Testresult.JPG

    As you can see in the screenshot above, it seems the system passes the service’s address to the internet, not the URL to the weather API.

    Thank you,

    Peter

    (0) 
  8. Subbarao Ilam

    Hi Paul,

    I have tried to implement this example with same API by registering in worldweatheronline.

    Some how when I try to execute the service call I am getting return code “1 =  http_communication_failure” from the method call

      lo_http_client->receive(

         EXCEPTIONS

           http_communication_failure = 1

           http_invalid_state         = 2

           http_processing_failed     = 3 ).


    Could you pleas let me know how to resolve this, are there any other setting/config we have to maintain before calling the API?


    Thanks in advance.


    Thanks,

    Subba,

    (0) 
    1. Paul J. Modderman Post author

      This is probably related to your HTTP client configuration. Here’s what I posted to another question from above:

      You should try program RSHTTP20 and put in the URI you’re trying to use there (I suggest using ‘http://www.sap.com/index.html‘ instead of sap.com, since sap.com redirects to the index page).

      What that program will do is go through the HTTP request process and tell you what kind of response you’re getting, or it will tell you if there’s an error in your SAP_HTTP RFC setup. I suspect that that’s where the error is – I have a test system that isn’t configured to make outbound HTTP calls and I get the same error you’re getting, but when I use a system properly configured to make outbound HTTP calls I can get a success from the RECEIVE method.

      Hope this helps.

      And I do – I hope this helps!

      Paul

      (0) 
            1. Ekansh Saxena

              Hi Subba,

              If you are using the corporate network, you might be using some proxy to connect to internet. If the proxy is in place, you need to set it up in the SAP also in order to communicate to internet. This was the case with me. So I chose to create a RFC destination with proxy and its credentials, then used cl_http_client=>create_by_destination instead of ‘create_by_url’ and then use cl_http_utility=>set_request_uri( request = lo_http_client->request uri = lv_service ) to set up the query URI.

              Regards,
              Ekansh

              (0) 
  9. Matthew Billingham

    Very helpful. I just created a method which takes service and query as string parameters and returns a result string parameter:

    So very simple.

      data service type string.
      service = me->url && i_service.
    
      data http_client type ref to if_http_client.
      cl_http_client=>create_by_url(
        exporting
          url                = service
        importing
          client             = http_client
        exceptions
          argument_not_found = 1
          plugin_not_active  = 2
          internal_error     = 3
          others             = 4 ).
    
      data: http_request type ref to if_http_request.
      http_request = http_client->request.
      http_request->set_method( 'POST' ).
      http_request->append_cdata( data = i_query ).
    
      http_client->send( ).
      http_client->receive( ).
    
      r_result = http_client->response->get_cdata( ).
    (1) 
  10. Yogendra Ahuja

    Hi Paul,

    Thanks for this post. Such a piece of gem.

    Well the weather API works fine as per your code, but when i try to invoke API for google analytic ( which gives output in JSON format), lo_http_client->receive is throwing an exception “http_communication_failure”,  any idea what could be the issue.


    Thanks

     

       

    (0) 
    1. Paul J. Modderman Post author

      Hi Yogendra,

      You should post your code. I’ll bet google analytics requires some hand-holding on authenticating requests to their API. Curious to see how you’re handling that (be sure to scrub any actual authentication data from your code).

      Paul

      (0) 
  11. Meghna Bhardwaj

    Hi paul,

    I wanted to understand if its mandatory to develop a gateway service , while consuming an external Odata service. If we have an ABAP program , to consume the external service as described by you, We can execute the program as is.

    Please share your views.

    Thanks

    Meghna

    (0) 
    1. Paul J. Modderman Post author

      Hi Meghna,

      I’m having a little trouble understanding your question. Are you asking if you should use an ABAP program that already consumes an external service as the driving logic for a Gateway OData service? If so, then yes I think you’d want to save the time and effort – and you can easily re-use existing ABAP code when you create Gateway services.

      But I’m not sure that I’m answering your question. Please let me know if I’m misunderstanding.

      Thanks!

      Paul

      (0) 
      1. Meghna Bhardwaj

        Hi Paul,

        Thank you for the quick reply.Sorry for the confusion.

        My doubt is , do we need to develop an SAP gateway service to consume an external rest service?Can we consume an external service by a stand alone ABAP program only,without any development of SAP gateway service.

        Do we need to develop an SAP gateway service at all to achieve this?I wanted to confirm if  a stand alone ABAP program should be able to consume an external REST service,without any additional gateway development.Correct me if i am wrong.

        Meghna

        (0) 
        1. Paul J. Modderman Post author

          Hi Meghna,

          OK – I understand your question. Thanks for clarifying.

          You do NOT need to develop an SAP Gateway service to consume an external REST service. Simply use the cl_http_client (or cl_rest_http_client, by example elsewhere) to make calls to that external service and then do whatever you want with that data. Anywhere you can write ABAP (function modules, report programs, classes, etc) can consume external REST services.

          Hope that helps!

          Paul

          (0) 
            1. Meghna Bhardwaj

              HI paul,

              If we have to pass some values in the header , like the api key and format.

              How can we achieve it.

              I have tried the following :

              CALL METHOD lo_http_client->request->set_method( if_http_request=>CO_REQUEST_METHOD_GET ).

                 “Set Header field

                 CALL METHOD lo_http_client->request->set_header_field

                   EXPORTING

                     name  = ‘API-KEY’

                     value = ‘XXXXXXXXX’.

                 “Set Header field

                 CALL METHOD lo_http_client->request->set_header_field

                   EXPORTING

                     name  = ‘Accept’

                     value = ‘application/xml’.

                 lo_http_client->send(

                 EXCEPTIONS

                 http_communication_failure = 1

                 http_invalid_state = 2 ).

                 lo_http_client->receive(

                 EXCEPTIONS

                 http_communication_failure = 1

                 http_invalid_state = 2

                 http_processing_failed = 3 ).

              But the code hangs indefinitely on the ‘Receive’ method.

              Meghna

              (0) 
  12. Meghna Bhardwaj

    HI paul,

    Can we use this for HTTPS service calls?

    If we have to pass some values in the header , like the api key and format.

    How can we achieve it.

    I have tried the following :

    CALL METHOD lo_http_client->request->set_method(if_http_request=>CO_REQUEST_METHOD_GET ).

       “Set Header field

       CALL METHOD lo_http_client->request->set_header_field

         EXPORTING

           name  = ‘API-KEY’

           value = ‘XXXXXXXXX’.

       “Set Header field

       CALL METHOD lo_http_client->request->set_header_field

         EXPORTING

           name  = ‘Accept’

           value = ‘application/xml’.

       lo_http_client->send(

       EXCEPTIONS

       http_communication_failure = 1

       http_invalid_state = 2 ).

       lo_http_client->receive(

       EXCEPTIONS

       http_communication_failure = 1

       http_invalid_state = 2

       http_processing_failed = 3 ).

    But the code hangs indefinitely on the ‘Receive’ method.

    Meghna

    (0) 
    1. Paul J. Modderman Post author

      Hi Meghna,

      I would try this on a simple known good https url – like https://www.google.com. Take out the header field params and just do the call that way. See if you get a real response…the problem could possibly be in your system’s overall HTTP setup.

      If you get the right stuff from the bare HTTPS request, then what I do is use a tool like Postman or Advanced Rest Client (both Chrome extensions, but you can find good stuff for Firefox or other browsers) and make sure my header parameters are appropriate for the web API I’m calling.

      If you’re willing to share more code I can probably help more.

      Paul

      (0) 
      1. Vijay Gupta

        Hi Paul,

        Thanks for this post.

        I have one scenario to be mapped in SAP. Can you please help me out?

        I need to call a JSON API (https://api-sandbox.grovo.com/users) from ABAP. This API needs a valid token which to be received from another API call (https://api-sandbox.grovo.com/token), after providing API id and password.


        for API details – https://grovo-api.readme.io/v1.0/docs


        For each API subsequent call I would need to call this Token API.

        Can you please help me understand which SAP function module / class to call to get token from (https://api-sandbox.grovo.com/token) API , and how to pass this token value to call subsequent API (https://api-sandbox.grovo.com/users))? 

        Thanks

        Vijay

        (0) 
  13. Vamsi Krishna C V

    Hi Paul,

    thank you for this Post. It helped me a lot.

    However i do have some questions.

    I am trying to call an OData Service with Authentication involved.

    I know we can use the authenticate method in the CL_HTTP_CLIENT class, but is there a way to use the Create_by destination method so the user/ID password details can be stored securely in an SM59 connection.

    While doing the SM59 Configuration, how can we pass the parameters to the URI?

    Have you considered/encountered such a scenario?

    Kind Regards,

    Vamsi

    (0) 
    1. Paul J. Modderman Post author

      Hi Vasmi,

      I see that you put a blog up recently on this topic – I’m sorry I was too late to respond to try to help you, but I’m glad that you put something out there that I could learn from. Well done!

      Regards,

      Paul

      (0) 
  14. jeff broodwar

    Sorry, I just dont have a way to ask pauls help and might be off topic if I post my concern here.I can create a new thread just want pauls attention thats all. Tnks

    (0) 
  15. Kritika Singh

    Hi Paul,

    Thanks for the blog. I have created a FM and using the code in my FM.

    But I’m getting a dump at this line.

      lo_weather_element = lo_document->find_from_name_ns( ‘weather’ ).

      lo_weather_nodes = lo_weather_element->get_children( ).

    The issue is the lo_weather_element is Initial and not having the corresponding node. I have checked doing F5 at this statement. But it goes to the below code and comes out of it.

    method if_ixml_document~find_from_name_ns
    by kernel module abkm_iXMLDocument_FindFromNameNS.
    endmethod.

    Can you please share some info on this about how to make this work.

    Thank you,

    Kritika

    (0) 
  16. Abhishek Sarkar

    Hello,

    The requirement is to consume a web service REST-POST. Base URL is provided along with other credentials which are constant value. Web service will return a JSON format which I need to map into ABAP structure. Following the above code and comments from Paul and other members, I tried to start with CREATE BY URL. It seems didn’t work and came to know that I need to trigger a POST method using FORM DATA. I have no clue how to achieve this using FORM DATA in order to prepare my URL.

    Once the URL is triggered and JSON is returned. It will be mapped into ABAP structure, so planning to use the /UI2/CL_JSON=> deserialize to convert the JSON data into ABAP structure.  Kindly let me know how to prepare the URL using POST method for JSON.

    (0) 
  17. Syambabu Allu

    Hi,

    I have implemented both GET/POST everything works as expected ..is that possible to implement as below PUT method.

    lo_client->request->set_method( ‘PUT’ ).

    Let me know if you have any advice’s on this.

    Thanks,

    Syam

    (0) 
  18. Rohini Sawhney

    Hi Paul,

    Thanks for the blog. I have created a FM using the above given code.

    But I’m getting a dump at this line.

    lo_weather_element = lo_document->find_from_name_ns( ‘weather’ ).

    lo_weather_nodes = lo_weather_element->get_children( ).

    I tried to execute the same in the debugger and found that the API key is invalid.

    I have generated the API key by registering as a (free) developer on https://developer.worldweatheronline.com/auth/register site.

    Could you please help by providing the appropriate solution.

    Thanks.

    Rohini

    (0) 
    1. Paul J. Modderman Post author

      Hi Rohini,

      It’s been a few years since I looked at this stuff. The world weather online service may have changed how it checks/supplies keys. You may need to switch “free” for “premium” in the URL for the api call.

      You might also make sure you’re using the right version of the API call in the URL. Since you signed up recently, you’ll need “v2” in the URL instead of “v1”. 

      If that doesn’t work, try using a tool like Postman to figure out how the API call needs to look before plugging that back into the ABAP code.

      Let me know if that helps!

      -Paul

      (0) 
  19. Vamsi Krishna

    HI Paul,

    {“Time”:”2017-03-13T05:05:39-04:00″,”Result”:”9″}

    This my webservice result in json format. Can anybody help me how to read this using ABAP? i tried with above piece of code but i am getting dump.

    
                                                
    (0) 
    1. Paul J. Modderman Post author

      Hi Vamsi,

      The above code expects the results of a web service call in XML format. If the service you’re calling is returning JSON, then may I suggest looking at the below for a call transformation solution to serializing/deserializing JSON<==>ABAP.

      E.g.  https://blogs.sap.com/2013/07/04/abap-news-for-release-740-abap-and-json/

      Also https://blogs.sap.com/2013/01/07/abap-and-json/

      Hope this helps!

      -Paul

      (0) 

Leave a Reply