Professional Documents
Culture Documents
SAP_ABAP_on_HANA_1695413609
SAP_ABAP_on_HANA_1695413609
***************************************************
SAP ABAP on HANA
Getting Started with the ABAP RESTful Application
Programming
Getting Started with the Model (RAP) Application
ABAP RESTful
Getting Started with the ABAP RESTful Application
The ABAP RESTful Applica2on Programming Model (short: RAP) offers developers an efficient way to build
enterprise ready, SAP HANA-op2mized, OData-based Fiori UI services and Web APIs in the cloud as well as
on-premise. It is the evolu2onary successor to the ABAP Programming Model for SAP Fiori.
The ABAP RESTful Programming Model is generally available in the SAP BTP ABAP Environment, and now
also in SAP S/4HANA star2ng with the edi2on 1909. The feature scope in SAP Business Technology
PlaVorm (BTP) ABAP Environment – formerly known as SAP Cloud PlaVorm ABAP environment – is
enhanced on a quarterly basis on defined dates, while new capabili2es are delivered in SAP S/4HANA on a
yearly basis with new on-premise edi2ons.
The greenfield development of OData-based services – i.e., star2ng from scratch – is supported with the
socalled managed implementa2on type, whereas the brownfield development – i.e., based on exis2ng
code – is supported with the so-called unmanaged implementa2on type.
The key players in the ABAP RESTful Applica2on Programming Model are:
Roadmap:
In two recent blogs, I demonstrated how to write web clients of REST APIs – with XML (demo applica2on
here) or JSON (demo applica2on here) as data transfer format. In this blog, I will focus on the server
side: How to implement a REST API as ABAP request handler. You can inspect all the code I am discussing
here on the MIGROS BSP website: It’s all in Class ZCL_JOB_DATA .
Our example service is afached to the path /job/afributes. The class ZCL_JOB_DATA is declared to be
responsible for all incoming requests where the request path starts with /job/afributes :
First Strategy: HTTP Request Method
The implementa2on of the interface method if_hfp_extension~handle_request() forms the uppermost
level of processing. Therefore, the implementa2on only gives the rough processing skeleton: An instance
for database opera2ons, as well as an instance for the processing of the REST opera2on are created, the
request handling is delegated to that instance, and there is a catch block for error processing in case that
no instance could be determined for processing the request. Such a situa2on should result in an HTTP
response with status code ‘400 – Bad Request’.
At this place, we are using the Strategy design pafern: Depending on the HTTP method (GET, PUT, POST,
DELETE, OPTIONS), a specific instance is created. Each possible instance corresponds to a specific strategy.
method if_hfp_extension~handle_request .
• Get the correct rest handler instance, depending on the verb (GET, PUT, POST, O lo_rest ?=
get_rest( io_server = server io_db = lo_db ).
• Do the opera2on lo_rest->handle_request( ). catch zcx_not_found into
• endtry. endmethod.
We are using a naming conven2on for the instance determina2on: The class LCL_REST_GET will be
associated with HTTP verb GET, LCL_REST_PUT with PUT, and so on. All these classes implement the
interface LIF_REST. This way, we can use dynamic instance crea2on. Alterna2vely, we could have wrifen a
large CASE … statement with many WHEN’s. The advantage of the CASE would be that the create object
statement could be sta2cally checked for syntac2cal correctness. I have chosen the dynamical variant
since I find it clearer and more readable than a bunch of WHEN branches.
Observe that the HTTP request method (GET, PUT, POST, …) is available as pseudo header field with the
name ‘~request_method’:
Now, some request methods like GET do not require any request content. So the conversion of incoming
data is performed by those method handlers that know they require content data. On the other hand,
there will always be a result of the following data type:
There may not always be entries in the job table. But not every component of this structure will be ini2al.
If there is no job table, then usually there will be a message. So the conversion of the result can always be
performed.
It makes sense to work with an abstract converter class, the specific subclasses containing the conversion
algorithms per content-type. This is the second applica2on of the Strategy pafern.
class lcl_converter defini2on abstract.
public sec2on. class-methods
get_instance
impor2ng
iv_accept type string returning value(eo_instance) type ref to
lcl_converter. methods content_type abstract returning
value(ev_content_type) type string. methods get_entered_data abstract
impor2ng
iv_cdata type string
expor2ng es_job type zjobs
raising zcx_parse_error. methods
result_to_cdata abstract
impor2ng
is_result type ty_result
The sta2c method LCL_CONVERTER=>GET_INSTANCE( ) makes the dis2nc2on, depending on the Accept
header field of the HTTP request:
Now, the common implementa2on lif_rest~handle( ) in the superclass only defines the flow of the
processing, leaving the concrete ac2ons to the subclasses or to delegates like go_converter:
This is the general sketch – the response processing that is valid for all HTTP request methods and for all
content types (XML as well as JSON). The details are contained in the called methods.
method lif_rest~handle_request.
try.
endmethod. "handle_request
The job with the specified ID is read from the database (if an ID was specified – for new jobs, this is
not the case),
The entered data will be parsed into an ls_job structure, using the appropriate go_converter instance,
And finally, the save() method is called. It is implemented in the superclass, since other request
methods use it, too.
class lcl_rest_put implementa2on. method do. data: ls_job type
zjobs, lv_id type zjobs-id. try. get_job_by_id( impor2ng
es_job = ls_job ). lv_id = ls_job-id. catch zcx_not_found.
endtry.
endmethod. "do
endclass. "lcl_rest_put IMPLEMENTATION
Note that the implementa2on of this task doesn’t care about the HTTP data structure, the format actually
in use, nor about the details of the transfer data format. It simply works with ABAP data structures ls_job
for the input and es_result for the output.
The appcontext returned from this func2on, can be passed as query parameter with every Ajax request.
On the server side, the session ID can be extracted from that parameter:
method get_session_id.
* Read the form field, provided by the Ajax request lv_app_context64 = io_server-
As a fallback, in line 22, the server->session_id is used. However, there will be a new server->session_id
for each request, which results in fresh session data with each dialogue step. If you really need session
management, it is essen2al that the session id is passed to the server.
It is a good idea to combine the session id with the login procedure: If the user authen2cates, his browser
receives a sessionid with a limited validity. That session-ID has to be passed with each successive REST
opera2on. In ABAP, it can be used to
store and retrieve session-specific data in the database table SSCOOKIE, via its database access class
CL_BSP_SERVER_SIDE_COOKIE.
This coupling of a session id with login is – roughly – the way how the REST API for the HP Quality Center
works.
For example, a PUT request from our test applica2on may send the following JSON data to the server:
{
"ID": "0001",
"REPID":"RSNAST00",
"VARID": "UXPD_KUBE_KV",
"PRIO": "2",
"RESTART": "X",
"DESCR": "Output all sales order confirma2ons",
"CONTACT": "Rainer
Zufall" }
If a string with this content is passed as “SOURCE XML” to ABAP’s CALL TRANSFORMATION statement, the
JSON will be parsed into an XML representa2on like this one (the format is easy to understand – I think a
detailled explana2on is not necessary here):
When processing an arbitrary XSLT transforma2on, with the CALL TRANSFORMATION statement, and
passing a JSON string as source, the XSLT will operate on this internal JSON-XML representa2on. It is easy
to transform such a JSON-XML document into ABAP data – to be more precise: to transform it into an
asXML representa2on of ABAP data. For example, consider the following XSLT transforma2on:
When applied to the JSON string, it will produce the following result:
cx_transforma2on_error.
endmethod. "get_entered_data
Many things can be done with the iden2ty transforma2on ID, with no need to define an own XSLT
transforma2on at all. If you can impose the JSON data structure to be used in the web applica2on, it is of
advantage to use such a “canonical” structure. For example, consider wrapping the JSON hash with the
job afributes into another hash, making it the value for some symbolic key name like “JOB”:
{
"JOB": {
"REPID": "RSNAST00",
"VARID": "UXPD_KUBE_KV",
"PRIO": "2",
"RESTART": "X",
"DESCR": "Output all sales order confirma2ons",
"CONTACT": "Rainer Zufall",
"ID": "0001"
}
}
Then the data could be parsed into a structure without the need of developing a custom XSLT
transforma2on, simple using the iden2ty:
call transforma2on id
source xml iv_cdata
result job = es_job.
In this example, since I have wrifen the web-client and the server-side processing, I could have chosen
this more “canonical” format. But by not chosing it, I learned how to work with more flexible JSON data
formats.
There are several reasons for working with “non-canonical” JSON representa2ons of ABAP data:
A JSON format may be designed in favour of the web applica2on – to op2mize the readability of the
client JavaScript code working on the data.
There may be client components requiring a par2cular JSON formats. For example, the jQuery
datatable requires the table data to be passed as an array of arrays:
hfp://www.datatables.net/releasedatatables/examples/data_sources/ajax.html
JSON-based third party services may be called from the ABAP side (with a HTTP client object)
ABAP data may be projected to the essen2al data, reducing the message size to the data which are
really needed.
Just to illustrate, let’s have a look at the other conversion – the way out from the server to the client.
Again, the format differs slightly from the “canonical” JSON format, which would simplify the ABAP-side
handling considerably. As men2oned, the result data structure contains
a
message,
a
message
type, and
a table of
job
afributes
:
The following format would be a perfect JSON pendant for this structure. It could be simply produced
with the iden2ty transforma2on, passing as “source result = ls_result” (where ls_result is a structure of
type ty_result):
All the component names match perfectly with the JSON hash key names,
An internal table is mapped as a JSON array of hashs, each hash represen2ng one
entry of the table, And there is a top level hash with a symbolic name “RESULT” for
the complete thing:
{
"RESULT": {
"MSGTYPE": "I",
"MESSAGE": "Test",
"JOBS": [
{
"ID": "0001",
"REPID": "ZZTEST",
"VARID": "VARI1",
"PRIO": "1",
"RESTART": "X",
"CONTACT": "Harry Haller",
"DESCR": "A hopeless job"
},
{
"ID": "0002",
"REPID": "ZZTEST2",
"VARID": "VARI2",
"PRIO": "3",
"RESTART": "",
"CONTACT": "Peter Pan",
"DESCR": "A juvenile job"
}
]
}
}
But the JSON format that the REST API supports, actually differs in some details:
{
"JOBS": {
"0001": {
"REPID": "RSNAST00",
"VARID": "UXPD_KUBE_KV",
"PRIO": "2",
"RESTART": "X",
"CONTACT": "Rainer Zufall",
"DESCR": "Output all sales order confirma2ons"
},
"0002": {
"REPID": "RBDAPP01",
"VARID": "UXPD_EDI_GUT02",
"PRIO": "3",
"RESTART": "X",
"CONTACT": "Herbert Hur2g",
"DESCR": "Credit Memos"
}
},
"MESSAGE": "",
"TYPE": ""
}
We proceed in a similar way as above, only in the other direc2on: based on the ABAP data type ty_result,
we write an XSLT transforma2on to obtain the internal JSON-XML format corresponding to this JSON data
string.
The JSON-XML data format of the desired JSON data string looks like this:
So this is the target that has to be obtained as result of the transforma2on. On the other hand, the asXML
format of the structure ty_result looks like this:
And this is the XSLT program that will perform the transforma2on:
<xsl:template match="DATA">
<object>
<xsl:apply-templates/>
</object>
</xsl:template>
<xsl:template match="JOBS">
<object name="JOBS">
<xsl:apply-templates/>
</object>
</xsl:template>
<xsl:template match="ZJOBS">
<object name="{./ID}">
<xsl:apply-templates select="*[name() != 'ID']"/>
</object>
</xsl:template>
<xsl:template match="ZJOBS/* | MESSAGE">
<str name="{name()}">
<xsl:value-of select="."/>
</str>
</xsl:template>
<xsl:template match="MSGTYPE">
<str name="TYPE">
<xsl:value-of select="."/>
</str>
</xsl:template>
</xsl:transform>
We see that, basically, for each devia2on from the “canonical” JSON representa2on of ABAP data, there is
a template in the XSLT transforma2on handling this devia2on. For example, the different name TYPE
instead of MSGTYPE in the target is handled with the template
<xsl:template match="MSGTYPE">
<str name="TYPE">
<xsl:value-of select="."/>
</str>
</xsl:template>
The ID has to be rearranged: From being a simple afribute of the ZJOBS data structure, it has to be raised
one level higher to become the key of a hash. All the other afributes, except ID, are copied as string
nodes into the result. For this, these two templates are necessary:
<xsl:template match="ZJOBS">
<object name="{./ID}">
<xsl:apply-templates select="*[name() != 'ID']"/>
</object>
</xsl:template>
Mapping the ty_result data object into a JSON string of the expected format, is now performed in ABAP
with the following code:
endmethod. "result_to_cdata
That’s all: ev_cdata will then contain the JSON data string, to be placed in the HTTP response body.
Summary
I outlined some typical topics concerning the implementa2on of REST APIs in ABAP. It is possible to keep
separate concerns in separate (local or global) classes by applying paferns like strategy. This is how the
class ZCL_JOB_DATA, serving my demo REST API, is organized (the basic ideas have been discussed in this
blog):
Consume API directly into ABAP
As API is gexng famous with so many APIs available how can we consume it into ABAP directly without
any middle wares.
One of the easiest way to call an external API directly into ABAP system using API Key.
SSL Cer2ficates – You can download the cer2ficate and it needs to install in SAP. In SAP, cer2ficates can be
installed using tcode Strust.
START-OF-SELECTION.
IF sy-subrc IS NOT
INITIAL. * Handle errors
ENDIF.
*Structure of HTTP Connec2on and Dispatch of Data lo_client->send( ). IF sy-subrc IS NOT INITIAL. *
Handle errors ENDIF.
*Receipt of HTTP Response lo_client->receive( ). IF sy-subrc IS NOT INITIAL. * Handle errors ENDIF.
END-OF-SELECTION.
*Return the HTTP body of this en2ty as binary data lv_xstring = lo_client->response-
>get_data( ).
Step 4 – Output
Once the program is executed, you will be able to see the data directly in form of XML. The XML data can
also be converted into internal table and be used/displayed as per requirement
Configuring OAuth 2.0 and Crea:ng an ABAP Program That
Uses OAuth 2.0 Client API
Introduc)on:
The OAuth 2.0 server (AS ABAP) protects resources you want to use, and the OAuth 2.0 client enables you
to access services and resources that are offered by a service provider.
Authen2ca2on with OAuth 2.0 protec2on between an SAP NetWeaver Applica2on Server for ABAP and
an external service provider such as, for example, SAP HANA Cloud PlaVorm, Google Cloud PlaVorm, or
MicrosoŠ Azure, requires a dedicated OAuth 2.0 client. You can configure and register this OAuth 2.0
client in the OAuth 2.0 server (AS ABAP).
The OAuth 2.0 client enables end users to easily access a service provider with the same creden2als they
are already using in the service provider. The communica2on between OAuth 2.0 client and server is
secured by an HTTPS connec2on. The end users can then use services and resources offered by a service
provider, for example, SAP HANA Cloud PlaVorm or MicrosoŠ Azure, to edit or process their data that is
located as resources on the AS ABAP. During the authen2ca2on, the OAuth 2.0 client passes the OAuth
2.0 scopes to the service provider. The OAuth 2.0 scopes contain references to the allowed resources.
So first, lets try to understand from POSTMAN. How to call the OAuth2.0 enabled endpoint.
POSTMAN:
Use the GET call with the main API endpoint. In the authen2ca2on, select the type as ‘OAuth2.0’.
Based on the service provider, select the grant type on the right hand side. I have selected as Client
Creden2als. Provide the Access Token URL, Client ID and Client Secrete. Also provide the scope as
configured at the service provider. Select Client Authen2ca2on as ‘Send as Basic Auth header’ and click
on Get New Access Token.
Now perform the GET call and set any header parameters if required.
We get the status as 200 and response from the service provider.
Now we will call the OAuth2.0 enabled endpoint from ABAP program using OAuth2.0 configura2on.
Refer to the SAP help which has quite good amount of informa2on on the process flow and pre-
requisites. hfps://help.sap.com/viewer/3c4e8fc004cb4401a4fdd737f02ac2b9/7.5.6/en-
US/90d8fa4c8b38425aae560d1d402fe627.html
We can also create the RFC des2na2on to maintain the Main API endpoint.
Here we will use the profile name and configura2on name as ‘ZOAUTH_CLIENT_PROFILE’ to set the
OAuth2.0 token.
Here is one of the way to call an external API and get the data into ABAP system.
Step 1. Use class CL_HTTP_CLIENT to get an object of type IF_HTTP_Client. There are different methods
for gexng this object. example: by the API URL or by HTTP Des2na2on etc. In below code snippet I have
used API URL to get the object. Once the object of type IF_HTTP_Client is received, then execute the
instance methods SEND() and RECEIVE() to establish Connec2on and Dispatch of Data and receive HTTP
Response.
REPORT zsuhas.
Note : If you might be asked to authen2cate while calling RECEIVE method. If so, insert the following
piece of code aŠer you get the create object of type IF_HTTP_client.
lo_client->authen2cate( expor2ng
proxy_authen2ca2on = 'X'
client = Client " R/3 System, client number from logon username = l_username "
ABAP System, User Logon Name
password = l_password " Logon ID
* language = language " SAP System, Current Language
).
Step 2 . Use get_data() method of the afribute response of object received in Step 1 to get the data in
Binary format. And store it in a variable of type xstring.
*Return the HTTP body of this en2ty as binary data lv_xstring = lo_client-
>response->get_data( ).
Step 4. Get the root node of your DOM structure and all its children. Use the iterator object to reach
your desired node. Once the desired node is reached, create another iterator object to iterate the desired
node. the constants “co_node_text”, “co_node_element” and “co_node_cdata_sec2on” help determine
what kind of node type is read.
At this stage the data of the desired node is available in your ABAP system to play with. I am using write
statement below to display the node element name and its corresponding value. Any opera2on can be
performed once the data is brought into your SAP system.
ENDDO
ABAP: How to consume external REST APIs
1
ABAP: How to consume external REST APIs
2
ABAP: How to consume external REST APIs
3
Consuming External API’s in ABAP Code Snippets
Obtaining the relevant details in order to get the Token
In order to get the Token, details like Applica2on ID, Applica2on Secret, Client ID and the Token Endpoint must be provided
by the Provider.
Applica2on ID, Client ID and Secret are simply data strings and the Token Endpoint is a URL.
The defini2on of which header parameters are needed in order to successfully call the Token Endpoint has to be provided by
the API Provider.
METHOD get_token.
DATA: lo_hfp_token TYPE REF TO if_hfp_client, l_response_token TYPE string.
DATA: l_status_code TYPE i.
DATA: lv_bas64enc TYPE string. DATA: lv_url_token
TYPE string, lv_client_id TYPE string.
DATA: lo_root TYPE REF TO cx_root.
TRY.
*Url to access token lv_url_token = IS_API_CREDENTI-token_endpoint. "Token Endpoint
IMPORTING
client = lo_hfp_token EXCEPTIONS
argument_not_found = 1 plugin_not_ac2ve =
2 internal_error = 3 OTHERS = 4.
IF sy-subrc <> 0.
* MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno* WITH sy-msgv1 sy-
msgv2 sy-msgv3 sy-msgv4.
*Close the client in case of Error lo_hfp_token-
>close( ). ENDIF.
* Set Method POST
IF lo_hfp_token IS BOUND. lo_hfp_token->request->set_method( if_hfp_en2ty=>co_request_method_po
ENDIF.
cl_hfp_u2lity=>encode_base64( EXPORTING
unencoded = lv_bas64enc RECEIVING
encoded = lv_bas64enc ).
lo_hfp_token->request->set_header_field( EXPORTING
name = 'Authoriza2on' value = lv_bas64enc
).
lo_hfp_token->request->set_header_field( EXPORTING
name = 'Content-Type'
value = 'applica2on/x-www-form-urlencoded'
). lo_hfp_token->request-
>set_header_field( EXPORTING name = 'Accept'
value = 'applica2on/json' ). *Set all the form
parameters lo_hfp_token->request->set_form_field(
EXPORTING name = 'grant_type' value = 'client_creden2als'
).
lo_hfp_token->request->set_form_field( EXPORTING
name = 'scope' value = 'client'
).
/ui2/cl_json=>deserialize( EXPORTING
json = l_response_token CHANGING
data = is_token "Token will be stored in the instance structur ).
lo_hfp_token->close( ).
CATCH cx_root INTO lo_root.
ENDTRY.
ENDMETHOD.
Calling an API with PUT opera2on using the Obtained token including Error Handling
In the following code, An API is called using the Token obtained above in order to write some data on the API Provider side.
Various return status codes which could come as return have also been processed.
Details of the Parameters which needs to be provided in order to make a call has to come from the API provider, Also the
details about the structure of the error data has to come from the API provider.
TRY.
lv_url_api = is_api_creden2-api_endpoint_new .
lo_hfp_put_empl->request->set_header_field( EXPORTING
name = 'Content-Type' value = 'applica2on/json'
).
CONCATENATE is_token-token_type is_token-access_token INTO lv_access_tok
lo_hfp_put_empl->request->set_header_field( EXPORTING
name = 'Authoriza2on' value =
lv_access_token
).
*Set body Parameter lo_hfp_put_empl->request->append_cdata( data = iv_empl_json ).
* Turn off logon popup. detect authen2ca2on errors. lo_hfp_put_empl-
>propertytype_logon_popup = 0.
OData, which is very popular in the SAP world, is itself a REST API. There is a lot of informa2on out there on how to provide
a REST API from ABAP (i.e., to publish an OData service). But there isn’t much on how to consume an external API in ABAP.
And from what lifle there is, it includes non-whitelisted ABAP APIs, i.e., they cannot be used with Cloud ABAP. So, I decided
to write this tutorial on consuming REST APIs using Cloud ABAP.
Scenario
API Provider
We will be working with JSON Placeholder – a “free to use fake Online REST API for tes2ng and prototyping”. It will allow us
to perform all CRUD (create, read, update, delete) ac2ons. To be fair, the create, update, and delete will not actually work,
but the server will fake it as if they do. Which is completely enough for our use-case!
Resources
Our API provider exposes posts, comments, albums, photos, TODOs, and users. For simplicity’s sake, we will only be using
the posts resource, and pretend the rest aren’t there. The main idea of my tutorial is to provide an extremely simple guide
on how to execute the CRUD ac2ons on a REST API. And do this using whitelisted ABAP APIs in the SAP Cloud PlaVorm (CP).
This means that you can run this code on a SAP CP trial account.
Posts resource
A post has an ID, 2tle, body and user ID, meaning the ID of the user that created the post. We represent it in ABAP as
follows:
TYPES:
BEGIN OF post_s, user_id TYPE i, id TYPE i, 2tle TYPE string,
body TYPE string, END OF post_s, post_f TYPE TABLE OF post_s
WITH EMPTY KEY,
METHODS: create_client
IMPORTING url TYPE string
RETURNING VALUE(result) TYPE REF TO if_web_hfp_client RAISING cx_sta2c_check
[implementa2on]
METHOD create_client.
DATA(dest) = cl_hfp_des2na2on_provider=>create_by_url( url ).
result = cl_web_hfp_client_manager=>create_by_hfp_des2na2on( dest ). ENDMETHOD.
No2ce that the URL is an input parameter. The returned result is the created web HTTP client.
The base URL is simply the address of the posts resource. The lafer two constants we need for the cases where we will be
sending data (i.e., create and update) to the server using the REST API. We have to let the server know we are sending
JSON.
Read all posts
The URL for reading all posts is just the base URL. So, we create a client for it, use the client to execute a GET request, close
the client, and convert the received JSON to a table of posts. The table of posts type is defined in the Posts resource sec2on
above. You can also refer to the full code at the end.
[defini2on]
read_posts
RETURNING VALUE(result) TYPE post_f
RAISING cx_sta2c_check
[implementa2on]
METHOD read_posts.
" Get JSON of all posts
DATA(url) = |{ base_url }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>get )->get_text( ). client->close( ).
hfps://jsonplaceholder.typicode.com/posts/{ID}
[defini2on]
read_single_post
IMPORTING id TYPE i
RETURNING VALUE(result) TYPE post_s
RAISING cx_sta2c_check
[implementa2on]
METHOD read_single_post.
" Get JSON for input post ID DATA(url) = |{ base_url }/{
id }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>get )->get_text( ). client->close( ).
" Convert JSON to post structure
xco_cp_json=>data->from_string( response )->apply(
VALUE #( ( xco_cp_json=>transforma2on->camel_case_to_underscore ) )
)->write_to( REF #( result ) ).
ENDMETHOD.
Create post
As explained earlier, posts’ IDs are automa2cally assigned by the REST API. So, to create a post we will be using the
post_without_id_s type. This will be our input parameter. We are going to convert from this ABAP structure to JSON, once
again using the XCO library. From there, we create a client. Then, we set the body of the HTTP request we are going to send
to be the JSON we just created and we let the server know that we will be sending JSON content-type. Lastly, we execute a
POST request, and return the server’s response. If all went good, the server’s response would return us our post, along with
its newly generated ID (101, because there are currently 100 posts).
[defini2on]
create_post
IMPORTING post_without_id TYPE post_without_id_s
RETURNING VALUE(result) TYPE string
RAISING cx_sta2c_check
[implementa2on]
METHOD create_post.
" Convert input post to JSON
DATA(json_post) = xco_cp_json=>data->from_abap( post_without_id )->apply(
VALUE #( ( xco_cp_json=>transforma2on->underscore_to_camel_case ) ) )->to_str
" Send JSON of post to server and return the response DATA(url) = |{ base_url }|.
DATA(client) = create_client( url ). DATA(req) = client->get_hfp_request( ). req->set_text( json_post ).
req->set_header_field( i_name = content_type i_value = json_content ).
Update post
We will be upda2ng with a PUT request. This means we will provide the full post. PATCH, on the other hand, allows us to
only provide the updated field (e.g., only 2tle). If you find this interes2ng, you could try to make the PATCH request yourself
– it shouldn’t be too hard with the provided here resources!
We follow a similar logic as with the create ac2on. We also provide a post as an input parameter, but this 2me we use the
full
structure (with post ID). The URL for upda2ng a post is the same as accessing this (single) post:
hfps://jsonplaceholder.typicode.com/posts/{ID}
So, the only differences from create include the changed type of the post input parameter, the URL, and the HTTP request
method (PUT).
[defini2on]
update_post
IMPORTING post TYPE post_s RETURNING VALUE(result) TYPE string
RAISING cx_sta2c_check
[implementa2on]
METHOD update_post.
" Convert input post to JSON
DATA(json_post) = xco_cp_json=>data->from_abap( post )->apply(
VALUE #( ( xco_cp_json=>transforma2on->underscore_to_camel_case ) ) )->to_str
client->close( ). ENDMETHOD.
Delete post
Dele2ng a post is the simplest request. We simply take the ID, and send a DELETE HTTP request to the URL of the specific
post. To let the user if something goes wrong, we check the server’s response code (should be 200 – meaning OK).
[defini2on]
delete_post
IMPORTING id TYPE i
RAISING cx_sta2c_check
[implementa2on]
METHOD delete_post.
DATA(url) = |{ base_url }/{ id }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>delete ).
METHOD if_oo_adt_classrun~main.
TRY.
" Read
DATA(all_posts) = read_posts( ).
DATA(first_post) = read_single_post( 1 ).
" Create
DATA(create_response) = create_post( VALUE #( user_id = 7 2tle = 'Hello, World!' body = ':)' )
).
[…]
Conclusion
This ends the tutorial of how to consume REST APIs in Cloud ABAP. I hope it has been useful for you. If you feel there’s any
points of improvements, or you have any ques2ons or feedback for me, let me know in the comments!
Full code
TYPES:
BEGIN OF post_s, user_id TYPE i, id TYPE i, 2tle TYPE
string, body TYPE string, END OF post_s, post_f TYPE TABLE
OF post_s WITH EMPTY KEY,
METHODS: create_client
IMPORTING url TYPE string
RETURNING VALUE(result) TYPE REF TO if_web_hfp_client
RAISING cx_sta2c_check,
read_posts
RETURNING VALUE(result) TYPE post_f
RAISING cx_sta2c_check,
read_single_post
IMPORTING id TYPE i
RETURNING VALUE(result) TYPE post_s
RAISING cx_sta2c_check,
create_post
IMPORTING post_without_id TYPE post_without_id_s
RETURNING VALUE(result) TYPE string
RAISING cx_sta2c_check,
METHOD create_client.
DATA(dest) = cl_hfp_des2na2on_provider=>create_by_url( url ).
result = cl_web_hfp_client_manager=>create_by_hfp_des2na2on( dest ). ENDMETHOD.
METHOD read_posts.
" Get JSON of all posts
DATA(url) = |{ base_url }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>get )->get_text( ). client->close( ).
METHOD read_single_post.
" Get JSON for input post ID DATA(url) = |{ base_url }/{
id }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>get )->get_text( ). client->close( ).
METHOD create_post.
" Convert input post to JSON
DATA(json_post) = xco_cp_json=>data->from_abap( post_without_id )->apply(
VALUE #( ( xco_cp_json=>transforma2on->underscore_to_camel_case ) ) )->to_s
METHOD delete_post.
DATA(url) = |{ base_url }/{ id }|.
DATA(client) = create_client( url ).
DATA(response) = client->execute( if_web_hfp_client=>delete ).
Prepara2ons
Before start coding in ABAP, we must execute the following prepara2on steps.
Proceed as follows to install the exported SSL cer2ficate in your SAP system.
We have to create a RFC des2na2on of type G with the following technical sexngs:
Please note that you might have to configure a proxy server if you are in corporate network where access to external sites
and web services are restricted by firewall due to security reasons.
Maintain the des2na2on details based on your requirements. Below screenshot is for demo purpose only.
Configura2on of TLS / SSL parameters in SAP
TLS (Transport Layer Security) is used to secure communica2on between your applica2on program in SAP which acts as a
consumer proxy and the REST API to which we are interfacing with.
There are scenarios where the hosted REST services in the external server support different version of TLS like 1.0 / 1.1 /
1.2. Ensure the required TLS / SSL configura2ons are enabled in SAP to connect with the REST api. Unless otherwise you
may be not able to hit the api from SAP.
In modern api’s TLS version 1.0 is not supported. API integra2ons use TLS 1.1 as a minimum, but version 1.2 is
recommended.
You can test whether your integra2on is compa2ble at any 2me using the test environment (hfps://api-
testbed.giŠbit.com/papi/v1)
If your test calls fail with an SSL handshake failure or similar error in SM59 when tes2ng the connec2on, refer the F1
documenta2on or ICM Monitor ( TCode: SMICM ) for you to communicate with rest API. This type of failure may be caused
by an outdated language version or library being used that does not have support for newer TLS versions.
Afached OSS message discusses in detail about the SSL configura2ons to be enabled in the ABAP applica2on server.
hfps://launchpad.support.sap.com/#/notes/510007
Thats all about configura2ons to be maintained. In the next part of this blog series, we will see how to write the ABAP code
solu2on to consume these REST api’s using the standard handler CL_REST_HTTP_CLIENT and parser class /UI2/CL_JSON.
Example:
1. You are impor2ng the cer2ficate from google chrome and adding to the server – Do we need to do the same
ac2vity.
It depends on the user case in my case using Slack / Intercom was sufficient.
Please go through this blog series to get deeper understanding of how to achieve the above requirements. We
have long / strict set of configura2ons involved that are discussed in the blogs.
Yes, this is supported provided STRUST setup is enabled for the SAP system to act like client proxy.
Technical implementa2on:
In this sec2on, all steps are described that you must implement for calling an external SLACK based web service from ABAP.
First we send a GET request to the external REST api and then receive the response data in JSON format. AŠerwards this
JSON is converted into na2ve ABAP format.
CL_HTTP_CLIENT
CL_REST_HTTP_CLIENT
/UI2/CL_JSON
Step 1:
Create a new REST client object with necessary request headers and authoriza2on key.
Step 2:
We get a string variable that contains the response data in JSON format. To process the data in ABAP, it must be converted
into an appropriate ABAP structure or data object for which we use standard parser class
If the request is successful, convert the JSON string into ABAP na2ve internal table format using standard parser class
/ui2/cl_json which contains methods to perform serializa2on and de-serializa2on. Please read here to know more about
these opera2ons.
Catch here is, we need to create a local type or a global type with the afribute names as same as JSON response retrieve
from the REST api.
ENDIF.
Check that user name and password as specified in your program matches those of your service instance in external
server.
Proceed as follows to check if the SSL cer2ficate for the external server is installed correctly.
Disclaimer: Above ABAP code snippets are for illustra2on purposes only and these are untested. Please tailor/validate these
codes based on your business requirements for the produc2on purposes.