Skip to Content
Author's profile photo Paul J. Modderman

Consuming an External RESTful Web Service with ABAP in Gateway

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. 

Assigned Tags

      86 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Former Member
      Former Member

      Thanks Paul.

      It is working as expected now.

      Regards.

      Dhruv

      Author's profile photo Ashwin Dutt R
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Ashwin Dutt R
      Ashwin Dutt R

      Hello Paul,

      Thank you so much for sharing the information. I will try CUD operation and get back to you.

      Regards,

      Ashwin

      Author's profile photo Vishnu Kiran Reddy Chintala
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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. 

      Author's profile photo Vishnu Kiran Reddy Chintala
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      Thanks for posting your successful code!

      Author's profile photo Dharmin Doshi
      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.

      Author's profile photo raju m
      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>

      Author's profile photo Greg Gipson
      Greg Gipson

      Hello Paul,

      How do you call the GW service URI form ABAP?

      Thanks,

      Greg

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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? 

      Author's profile photo abilash n
      abilash n

      Thanks Paul and Vishnu for nice share.....

      Author's profile photo Greg Gipson
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Greg Gipson
      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

      Author's profile photo Syambabu Allu
      Syambabu Allu

      Hi Paul,

      Good Blog..we are waiting for this 🙂

      Thanks,

      Syam

      Author's profile photo Former Member
      Former Member

      Thanks for the Great blog Paul

      Thanks

      Sri

      Author's profile photo Kevin Deuel
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Kevin Deuel
      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?

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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!

      Author's profile photo Kevin Deuel
      Kevin Deuel

      Groovy, you rock man! Consider yourself "followed" ... maniacle laugh manicale laugh

      Author's profile photo Andre Fischer
      Andre Fischer

      Nice post !

      Author's profile photo Former Member
      Former Member

      Thanks Paul ,.Great Job,.,

      Author's profile photo Stephen Herlick
      Stephen Herlick

      Hi Paul;

      Thank you for putting together this example.  I tried to recreate the function module in my test system.  I discovered that the link below no longer works.  The v1 has been changed to v2 and the output is slightly different.  Can you update your code to get the some of the fields from the new lower "hourly" link.

      Thanks

      Stephen

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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      Hi Stephen,

      The link still works for me - provided that I give it the API key and some query information.  Do you get some kind of error message when you try it in your code?  Trying it from the browser would require a URL of the form:

      http://api.worldweatheronline.com/free/v1/weather.ashx?key=[useyourownkeynotmine]&q=55426

      Paul

      Author's profile photo Stephen Herlick
      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

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Peter Simm
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      Hi Peter,

      Do you mind posting your code? I am a little confused by your wording. 

      Thanks,

      Paul

      Author's profile photo Former Member
      Former Member

      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 www.sap.com here in this post rest all is same.

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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. 

      Author's profile photo Former Member
      Former Member

      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.

      Author's profile photo Peter Simm
      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

      Author's profile photo Subbarao Ilam
      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,

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Subbarao Ilam
      Subbarao Ilam

      Hello Paul,

      Thanks a lot for quick reply, I ran the program RSHTTP20 with my URL given below

      http://api.worldweatheronline.com/free/v2/weather.ashx?q=91722&format=xml&key=<myapikey>

      I got the response but still I am getting the same connection error for RECEIVE method. Can you please adivse?

      Thanks,

      Subba

      Author's profile photo Ekansh Saxena
      Ekansh Saxena

      Hi Subba,

      Please check if you are behind some proxy.

      Regards,
      Ekansh

      Author's profile photo Subbarao Ilam
      Subbarao Ilam

      Hi Ekansh,

      what do you mean by behind proxy? can you please explain more?

      Thanks,

      Subba.

      Author's profile photo Ekansh Saxena
      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

      Author's profile photo Matthew Billingham
      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( ).
      Author's profile photo Former Member
      Former Member

      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

       

         

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Former Member
      Former Member

      Thank you ! that clarifies it.

      and thanks for sharing all the details in this blog.

      It really helps!

      Meghna

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo ashish chanekar
      ashish chanekar

      Hi Paul,

      just came across your blog - have some query in this regards,

      1. if we call the API directly from an ABAP program with out the gateway service then is it possible to trace the logging of request and response parameters as we get it when we create an API in ABAP and call it via SICF service without the gateway.

      would appreciate your reply.

      Thanks,

      Ashish.

       

      Author's profile photo Former Member
      Former Member

      Hi Paul,

      Thanks for the post. I have a question, can I do similar code for a "https" service call.

      Thanks,

      JM

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Vijay Gupta
      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

      Author's profile photo Vamsi Krishna C V
      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo jeff broodwar
      jeff broodwar

      Hello Paul,

      I find the article interesting, hope you can send private message, need your help. thanks in advance.

      Author's profile photo Matthew Billingham
      Matthew Billingham

      This is a site for sharing information. Do not ask for private messages.

      Author's profile photo jeff broodwar
      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

      Author's profile photo Matthew Billingham
      Matthew Billingham

      This is one way of attracting his attention. Paging Paul J. Modderman 🙂

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      I engaged with Jeff privately on this. If the discussion is at all worthwhile to be made generic and shared I will absolutely do so.

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Abhishek Sarkar
      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.

      Author's profile photo Syambabu Allu
      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

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Vamsi Krishna
      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.

      
                    
      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog 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

      Author's profile photo Amit Kumar Singh
      Amit Kumar Singh

      @Paul J. Modderman: Thanks for the great blog post.

      I want to ask here you have provided URL manually in the program code. This approach is not practical for typical business application. How to achieve the same via reading configuration done on gateway.

      Another point what if at design time the structure of requested resource from web service is not known, so what would be approach to consume it. Do we have enriched client side library in SAP so less parsing is required in such case?

      Thanks for your help.

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      Hi Amit,

      You can achieve URL configuration in any number of ways. A custom table, a TVARV value...anything like that. Store the URL as a string in one of those places, and read it at runtime.

      If you don't know at design time what the structure will look like, you can look at an approach like the one in this git repository: https://gist.github.com/mydoghasworms/2291540 . Method "json_to_data" should let you have any json be output as generic ABAP data. Though if you don't know the structure of what you're asking for from the web service, it may be hard to extract the right thing from that structure for your use.

      Paul

      Author's profile photo Former Member
      Former Member

      Hi Paul,

      Thanks alot for the wonderful post.

      I tried your example but i am getting initial value when executing this statement (  lo_weather_element = lo_document->find_from_name_ns( ‘Weather’ ).

      I looked at the blog and tried /o/IWFND/GW_CLIENT the transaction and executed it by giving the URL then i received an error “HTTP Receive failed: SSL handshake with api.worldweatheronline.com:443 failed: SSSLERR_PEER_CERT_UNT” .

      Can you please let me know how i can resolve this error.

      But when I execute same URL from Program RSHTTP20 i am getting connect Error: Open SSL not available.

      Thanks,

      Kiran

       

      Author's profile photo Kevin Dass
      Kevin Dass

      Hi Paul,

      Firstly good blog. Guess this was needed when T-code "/IWBEP/OCI_SRV_GEN" wasn't available. However would like to know if you try to register external OData service( example XS Engine Odata) onto Gateway and consume on SAPUI5 app on gateway? As when I did only HHTP GET methods works and no POST and other HTTP methods

      Reference: http://www.saplearners.com/how-to-consume-external-odata-services-in-sap-netweaver-gateway/

       

      Author's profile photo Former Member
      Former Member

      Hi Paul,

      This is something new to us. We have a requirement to access to a portal using REST API with login id and password and read the data from that portal. We are not understanding from where to start with. Do we first need to create a service in SEGW and link a method/FM of ABAP to it? Bit confused.

      Is it possible to provide your email address from where we can contact you?

      Thanks,

      Madhura.

       

       

      Author's profile photo Former Member
      Former Member

      Hi Paul,

      Thanks for posting such a valuable post.But my question is how to post complex xml like following

      <?xml version="1.0" ?>
      <AccessRequest xml:lang='en-US'>
      <AccessLicenseNumber>XXXXXXXXXXXXXXXXXXX</AccessLicenseNumber>
      <UserId>XXXXXXXX</UserId>
      <Password>XXXXXXX</Password>
      </AccessRequest>
      <?xml version="1.0" ?>
      <AddressValidationRequest xml:lang='en-US'>
      <Request>
      <RequestAction>XAV</RequestAction>
      </Request>
      <AddressKeyFormat>
      <AddressLine>100 Broad Street</AddressLine>
      <Region></Region>
      <PoliticalDivision2>NEWARK</PoliticalDivision2>
      <PoliticalDivision1>NJ</PoliticalDivision1>
      <PostcodePrimaryLow>75050</PostcodePrimaryLow>
      <CountryCode>US</CountryCode>
      </AddressKeyFormat>
      </AddressValidationRequest>

       

      Please give me some advice

      Thankyou so much

       

      Author's profile photo Vamsi Krishna C V
      Vamsi Krishna C V

      HI Paul,

      I need to pass an authorization header in a HTTP Post service.

      When i use the create_by_destination method, it always triggers a logon popup even when "NO Logon" is chosen in the RFC destination definition.

       

      I have used the propertytype_no_logon_popup but it does not suppress the logon popup.

      but whenever i call with create_by_destination the logon popup is always triggered.

      we cannot use the create_by_url option because, we do not want to hardcode the URL.

      Is there any way to configure the RFC destination so that, it does not trigger the popup and just uses the Authorization header?

      Kind Regards,

      Vamsi

      Author's profile photo Sankara Bhatta
      Sankara Bhatta

      HI,

       

      I am trying to use class CL_HTTP_CLIENT to trigger SAP cloud workflow from ABAP. I am able to get the CSRF token part successfully but the second POST call fails with HTTP 403 Forbidden error. where I am going wrong?

      https://answers.sap.com/questions/615947/trigger-scp-from-abap-program.html

       

      Author's profile photo Atul Mohanty
      Atul Mohanty

      Hi Paul,

      Hope you are doing good.

      I was following your this blog and was tryinging to replicate. But I am facing some issues. Could you please guide me further.

      1. I have registered as a developer in the website www.worldweatheronline.com to use API.
      2. I got an key till valid 6th Oct 2018.
      3. Using the API explorer in www.worldweatheronline.com & postman application, I can see xml file.
      4. I am using the following url ( I supress the key as 'mykey' 
        SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S32 {
        color: #3399FF;
        }
        .L0S55 {
        color: #800080;
        }
        .L0S70 {
        color: #808080;
        }
        http://api.worldweatheronline.com/premium/v1/weather.ashx?q=London&format=xml&date=2018-08-21&key=mykey
      5. However, when I am trying to use the code in SAP, I am getting error code '401'. Below is the code. 
        SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S31 {
        font-style: italic;
        color: #808080;
        }
        .L0S52 {
        color: #0000FF;
        }
        .L0S55 {
        color: #800080;
        }
        DATAlo_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,
        im_zipcode         TYPE        ad_pstcd1,
        *        ls_weather         TYPE        zweather,
        lv_node_name       TYPE        string,
        lv_node_value      TYPE        string.

        SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S33 {
        color: #4DA619;
        }
        .L0S55 {
        color: #800080;
        }
        lv_service 'http://api.worldweatheronline.com/premium/v1/weather.ashx?q=London&format=xml&date=2018-08-21&key= mykey'
      6. SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S52 {
        color: #0000FF;
        }
        .L0S55 {
        color: #800080;
        }
        Data :lv_rc TYPE i.

      7. SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S32 {
        color: #3399FF;
        }
        .L0S52 {
        color: #0000FF;
        }
        .L0S55 {
        color: #800080;
        }
        .L0S70 {
        color: #808080;
        }
        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             ).

        lo_http_client->send(
        EXCEPTIONS
        http_communication_failure 1
        http_invalid_state         ).

        lo_http_client->receive(
        EXCEPTIONS
        http_communication_failure 1
        http_invalid_state         2
        http_processing_failed     ).
        SPAN {
        font-family: "Courier New";
        font-size: 10pt;
        color: #000000;
        background: #FFFFFF;
        }
        .L0S31 {
        font-style: italic;
        color: #808080;
        }
        .L0S52 {
        color: #0000FF;
        }
        .L0S55 {
        color: #800080;
        }
        .L0S70 {
        color: #808080;
        }

      8. lo_http_client->response->get_status( IMPORTING code = lv_rc ).

        "prepare xml structure
        CLEAR lv_result .
        lv_result = lo_http_client->response->get_cdata( ).

       

      I am getting error code '401.

      Message as follows 'Description: Authorization is required for access to this proxy'.

       

      But when I execute, the URL in the program 'RSHTTP20' , I am getting return code as 200.

      Could you please guide, what could be the issue behind and how to resolve it ?

      Thank you.

      Regards,

      Atul M

       

       

       

       

       

       

       

      Author's profile photo Kenneth Moore
      Kenneth Moore

      If someone has done this with SAP Concur API's, I'd love it if they could share the code!

      Author's profile photo Alan Indomenico
      Alan Indomenico

      Hello to all , i use this code , and works really good , i can't thanks enough , but i have a problem when i get my data , is in string form , i use this

       


      SPAN {
      font-family: "Courier New";
      font-size: 10pt;
      color: #000000;
      background: #FFFFFF;
      }
      .L0S31 {
      font-style: italic;
      color: #808080;
      }
      .L0S32 {
      color: #3399FF;
      }
      .L0S33 {
      color: #4DA619;
      }
      .L0S55 {
      color: #800080;
      }
      .L0S70 {
      color: #808080;
      }
      lo_weather_element lo_document->find_from_name_ns('search_api').
      lo_weather_nodes lo_weather_element->get_children).

      lv_node_length lo_weather_nodes->get_length)"  CUANTAS FILAS HAY
      lv_node_index 0.

       


      SPAN {
      font-family: "Courier New";
      font-size: 10pt;
      color: #000000;
      background: #FFFFFF;
      }
      .L0S55 {
      color: #800080;
      }
      .L0S70 {
      color: #808080;
      }
        lo_curr_node  lo_weather_nodes->get_itemlv_node_index ).
      lv_node_name  lo_curr_node->get_name).
      lv_node_value lo_curr_node->get_value).

       

      The item properly is RESULT but if i put result is getting separeted in columns as the example says , but , when i use this "search api" is getting me the right amount of rows or items , but all 1 place , is a string with no separetor .

       

      Can someone please give a hand

      Author's profile photo vikash pathak
      vikash pathak

      Hello ,

       

      i implemented your code and got api as well , but getting error , i attached snap of api and error , please let me know if i have missed anything

      TYPES : BEGIN OF t_weather,
                forecast_date  TYPE dats,
                hight_temp_f   TYPE int4,
                low_temp_f     TYPE int4,
                wind_speed     TYPE int4,
                wind_direction TYPE char3,
                description    TYPE char40,
                precipitation  TYPE decfloat16,
              END OF t_weather.
      
      DATA:et_weather TYPE STANDARD TABLE OF t_weather.
      DATA: IM_ZIPCODE TYPE  AD_PSTCD1 VALUE '123456'.
      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 t_weather,
            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=[84494a1952094d158a582104200309]'.
      
      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_weather-forecast_date = lv_node_value.
          WHEN 'tempMaxF'.
            ls_weather-hight_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-wind_direction = 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.

       

      Author's profile photo Paul J. Modderman
      Paul J. Modderman
      Blog Post Author

      It looks like you hard-coded square brackets around your API key. That might be the issue? The API key in your URL shouldn't have brackets.

      Author's profile photo Ashok Sahu
      Ashok Sahu

      Hi Paul,

      I hope you are safe and doing well!!

      i am new to making HTTP calls from ABAP to external RESTful API Services. I have a requirements where I have been given a service /sap/opu/odata/IBP/PLANNING_DATA_API_SRV/. I have to use this to post data back. Please let me know how I should proceed. i have created a sample ABAP program as shown below:

      *************************************************************************************
      **********************************CODING FOR THE GET METHOD***********
      *************************************************************************************
      *Create the HTTP client
      cl_http_client=>create_by_destination(
      EXPORTING
      destination = c_rfcdest " Logical destination (specified in function call)
      IMPORTING
      client = lo_http_client " HTTP Client Abstraction
      EXCEPTIONS
      argument_not_found = 1
      destination_not_found = 2
      destination_no_authority = 3
      plugin_not_active = 4
      internal_error = 5
      OTHERS = 6
      ).

      CREATE OBJECT lo_rest_client
      EXPORTING
      io_http_client = lo_http_client.

      * Create request instance
      lo_request = lo_rest_client->if_rest_client~create_request_entity( ).

      * Set HTTP version
      lo_http_client->request->set_version( if_http_request=>co_protocol_version_1_0 ).

      IF lo_http_client IS BOUND AND lo_rest_client IS BOUND.
      *Fill headers and set URI for GET Method
      *Create URI for the client
      lv_query = 'getTransactionID'.

      cl_http_utility=>set_request_uri(
      EXPORTING
      request = lo_http_client->request
      uri = lv_query ).

      ENDIF.

      *Update the HTTP method
      lo_http_client->request->set_method( method = lo_http_client->request->co_request_method_get ).
      *********************************
      *set Content type
      lo_http_client->request->if_http_entity~set_content_type(
      content_type = 'application/json' ).

      lo_http_client->propertytype_accept_cookie = if_http_client=>co_enabled.
      *
      *set header field for festching x-csrf-token
      lo_http_client->request->set_header_field(
      name = 'x-csrf-token' " Name of the header field
      value = 'Fetch' ). " HTTP header field value

      *Trigger the GET Method
      lo_http_client->send(
      EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state = 2
      http_processing_failed = 3
      http_invalid_timeout = 4
      OTHERS = 5
      ).

      lo_http_client->receive(
      EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state = 2
      http_processing_failed = 3
      OTHERS = 4
      ).
      IF sy-subrc <> 0.
      MESSAGE e000(oo) WITH 'Processing failed !'.
      ENDIF.

      DATA(wf_string1) = lo_http_client->response->get_cdata( ).
      *
      *Get x-csrf token from earlier response
      lv_token = lo_http_client->response->get_header_field('x-csrf-token'). " Name of the header field

      lv_session = lo_http_client->response->get_header_field('set-cookie').

      *Convert JSON response to XML
      DATA: json TYPE xstring.

      *Return the HTTP body of this entity as binary data
      json = lo_http_client->response->get_data( ).

      CALL TRANSFORMATION id SOURCE XML json
      RESULT XML DATA(xml).

      DATA(reader) = cl_sxml_string_reader=>create( json ).
      DATA(writer) =
      CAST if_sxml_writer( cl_sxml_string_writer=>create( ) ).
      TRY.
      DO.
      CLEAR lwa_node.
      DATA(node) = reader->read_next_node( ).
      IF node IS INITIAL.
      EXIT.
      ENDIF.
      writer->write_node( node ).
      CASE node->type.
      WHEN if_sxml_node=>co_nt_element_open.
      DATA(open_element) = CAST if_sxml_open_element( node ).
      DATA(attributes) = open_element->get_attributes( ).

      LOOP AT attributes INTO DATA(attribute).
      lwa_node-node_type = `attribute`.
      lwa_node-prefix = attribute->prefix.
      lwa_node-name = attribute->qname-name.
      lwa_node-nsuri = attribute->qname-namespace.
      IF attribute->value_type = if_sxml_value=>co_vt_text.
      lwa_node-value = attribute->get_value( ).
      ELSEIF attribute->value_type =
      if_sxml_value=>co_vt_raw.
      lwa_node-value_raw = attribute->get_value_raw( ).
      ENDIF.
      IF lwa_node-value = 'LOCDESCR' OR lwa_node-value = 'LOCID'.
      APPEND lwa_node TO lt_node.

      ENDIF.
      ENDLOOP.
      CONTINUE.
      WHEN if_sxml_node=>co_nt_element_close.
      DATA(close_element) = CAST if_sxml_close_element( node ).
      CONTINUE.
      WHEN if_sxml_node=>co_nt_value.
      DATA(value_node) = CAST if_sxml_value_node( node ).
      lwa_node-node_type = `value`.
      IF value_node->value_type = if_sxml_value=>co_vt_text.
      lwa_node-value = value_node->get_value( ).
      ELSEIF value_node->value_type = if_sxml_value=>co_vt_raw.
      lwa_node-value_raw = value_node->get_value_raw( ).
      ENDIF.
      APPEND lwa_node TO lt_node.
      CONTINUE.
      WHEN OTHERS.
      lwa_node-node_type = `Error`.
      APPEND lwa_node TO lt_node.
      EXIT.
      ENDCASE.
      ENDDO.
      CATCH cx_sxml_parse_error INTO DATA(parse_error).

      ENDTRY.

      * GET TRANSACTION ID
      DESCRIBE TABLE lt_node LINES DATA(lv_lines).
      READ TABLE lt_node INTO DATA(lv_val) INDEX lv_lines.
      IF sy-subrc = 0.
      DATA(tranid) = lv_val-value. "transaction ID
      ENDIF.
      ************************END OF LOGIC TO GET TRANSACTION ID**************************

      *****************************************************************************************************
      ******************* LOGIC TO SEND DATA USING POST MTHOD *****
      *****************************************************************************************************

      free lo_http_client.
      CONCATENATE 'https://.......scmibp.ondemand.com/sap/opu/odata/IBP/PLANNING_DATA_API_SRV/'
      '(' '''' tranid '''' ')' INTO lv_url.
      cl_http_utility=>set_request_uri(
      EXPORTING
      request = lo_http_client->request " HTTP Framework (iHTTP) HTTP Request
      uri = lv_url " URI String (in the Form of /path?query-string)
      ).
      ENDIF.

      ************************LOGIC TO CREATE NESTED JSON PAYLOAD*******************************
      *** ABAP to JSON
      *Create the nested JSON file to be used for data update
      IF tranid <> space.
      DATA(nav_unipa_2021) = VALUE t_nav_unipa_2021( ( keyfigure = 'CONSENSUSDEMANDQTY'
      keyfiguredate = '2021/02/25'
      product = 'E6603GA0'
      customer = '54000'
      location = '5450' ) ).
      DATA(resp) = VALUE ty_response( transactionid = tranid
      aggregationlevelfieldstrings = 'KEYFIGURE, KEYFIGUREDATE, PRODUCT, CUSTOMER, LOCATION'
      navhasbroio = nav_unipa_2021 ).

      DATA(json_response) = /ui2/cl_json=>serialize(
      data = resp
      ).
      **********************END OF LOGIC TO CREATE NESTED JSON PAYLOAD*********************

      *Update the HTTP method
      CALL METHOD lo_http_client->request->set_method
      EXPORTING
      method = lo_http_client->request->co_request_method_post.

      *set the token received from GET method
      CALL METHOD lo_http_client->request->set_header_field
      EXPORTING
      name = 'x-csrf-token'
      value = lv_token.

      *set the cookie received from the GET Method
      CALL METHOD lo_http_client->request->set_cookie
      EXPORTING
      name = 'set-cookie'
      value = lv_session.

      *content type
      CALL METHOD lo_http_client->request->set_content_type
      EXPORTING
      content_type = 'application/json'.

      **Set body for the HTTP post request
      lo_http_client->request->set_cdata( data = json_response ).

      *Trigger the POST Method
      lo_http_client->send( "Send the HTTP request
      EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state = 2
      http_processing_failed = 3
      http_invalid_timeout = 4
      OTHERS = 5
      ).

      lo_http_client->receive( "receive the response
      EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state = 2
      http_processing_failed = 3
      OTHERS = 4
      ).

      DATA(l_result) = lo_http_client1->response->get_cdata( ).

      write:/ l_result.
      ENDIF.

      Need your help. Please assist. i am stuck and dont know how to proceed now.

      Thanks,

      Ashok

      Author's profile photo Jigang Zhang 张吉刚
      Jigang Zhang 张吉刚

      thanks for sharing this, very helpful!