Consuming an External RESTful Web Service with ABAP in SAP NetWeaver 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 ...

Contact us

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:
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_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.

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:
Note especially that the GetEntity method is mapped with zip code as the incoming parameter:
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 :
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.
Paul J. Modderman

Paul J. Modderman

Paul Modderman loves creating things and sharing them. He has spoken at SAP TechEd, multiple ASUG regional events, ASUG Fall Focus, Google DevFest MN, Google ISV Days, and several webinars and SAP community gatherings. Paul's writing has been featured in SAP Professional Journal, on the SAPinsider blog, and the popular Mindset blog. He believes clear communication is just as important as code, but also has serious developer chops. His tech career has spanned web applications with technologies like .NET, Java, Python, and React to SAP soutions in ABAP, OData and SAPUI5. His work integrating Google, Fiori, and Android was featured at SAP SAPPHIRE. Paul was principal technical architect on Mindset's certified solutions CloudSimple and Analytics for BW. He's an SAP Developer Hero, honored in 2017. Paul is the author of two books: Mindset Perspectives: SAP Development Tips, Tricks, and Projects, and SAPUI5 and SAP Fiori: The Psychology of UX Design. His passion for innovative application architecture and tech evangelism shines through in everything he does.

Let’s make your SAP better, together.

  • This field is for validation purposes and should be left unchanged.