Usage of the ABAP REST Library [SAP_BASIS 7.40]
Overview
Objective: Provides a brief introduction to REST services in general and explains the basic usage of the ABAP REST library (SAP_BASIS 7.40) in particular.
Applies to: SAP NetWeaver AS ABAP since 7.00. The SAP documentation for REST is available in AS ABAP 7.40.
Target Group: Develpers, architects, consultants, project leads and decision makers who want to gain first information on the ABAP REST library.
Introduction
This blog post gives a short introduction to the new ABAP library for REST based services, which is part of ABAP 7.4. In particular in combination with the new JSON support in ABAP 7.4, it can be used writing native HTTP services directly on top of the Internet Communication Framework (ICF) following a RESTful design.
The REST library is used as a central component in the following frameworks:
- SAP NetWeaver Gateway as a core of the OData support (OData is the REST-based protocol recommended by SAP for business scenarios).
- ABAP Development Tools for SAP NetWeaver (also known as ABAP in Eclipse) as part of its connectivity infrastructure, which is treating ABAP development objects as resources.
It should be stressed that the REST library is a technical library and does not provide a programming, configuration or operations model on top of the ICF.
For productive business scenarios SAP recommends to use the SAP NetWeaver Gateway which provides all qualities for REST services based on the OData standard.
Note that the core SAP NetWeaver Gateway infrastructure is also directly part of the SAP NetWeaver Application Server ABAP since release 7.4.
What is REST?
REST is an acronym for Representational State Transfer and stands for a programming paradigm for Web applications. The REST architectural style was first introduced by Roy Fielding (Link).
The central principle of REST services is the existence of resources each of them referenced with a unique resource identifier (URI). The reading and manipulation of these resources is based on a standardized interface which is the HTTP protocol. Via the HTTP protocol, client and servers exchange and transfer representations of resources, i.e. documents conveying the information of a resource.
When to use it?
There is a lot of information available in the Web about that question (and the alternatives to REST, e.g. SOAP based Web Services), so only some main indicators should be mentioned here:
- you have a stateless service,
- the support of (web) caching infrastructures is possible,
- the commmunication design is based on a point-to-point integration with no formal description of an interface, i.e. clients and servers agree out of band on the data format exchanged (e.g. in SOAP based Web Services, the data exchange format is pre-defined),
- frontend technologies may be of specific interest, i.e. REST services may be easier integrated and consumed by frontend platforms using their own technical basis (e.g. plain XML).
The ABAP REST Library
The ABAP REST library specific classes are located in package SREST and its subpackages (SREST_CORE, SREST_EXT, SREST_HTTP, SREST_TEST, SREST_UTIL). If you want to use the ABAP REST library, you have to declare an access usage for the corresponding package interface. Using the ABAP REST library, the focus in this blog is on developing REST services in the context of the HTTP protocol, i.e. the URI schema is http or https, the authority identifies an ABAP application server, the SAP web dispatcher or some network intermediate (e.g. reverse proxy, which is set up to forward a request to an ABAP application server). The path consists of the base path (defined in the ABAP Internet Communication Framework (ICF) for a REST service’s handler class) and the resource path, used by a REST application service to identify the requested resource.
ABAP REST Service Processing Overview
This picture gives a rough overview on the control flow when processing a REST request within an ABAP application server.
These are the 3 main processing steps (see picture above):
- Any incoming HTTP request on an ABAP application server is processed by the Internet Communication framework (ICF). The ICF contains a service registration where all ICF related services and their handler classes are registered (done via transaction SICF). The handler class is an ABAP class which implements the interface method IF_HTTP_EXTENSION~HANDLE_REQUEST.
This is also the case for ABAP REST services, where the ABAP handler class is derived from class CL_REST_HTTP_HANDLER. - Within the HTTP handler method HANDLE_REQUEST:
- The HTTP request object and response object get wrapped by corresponding REST request and response objects and are available to the REST service processing later on,
- a REST context is created with general information (URI parts, ICF service name, etc.) to be available in the REST service processing,
- the REST application is called containing its list of addressable resources with their identifiers and processing class(es),
- the REST router finds the processing class for the used URL,
- the application’s method to access the resource is called based on the used HTTP verb (GET, POST, PUT, DELETE, HEAD, OPTIONS).
- Inside the identified resource class and method, the data processing takes place.
REST Service Implementation
Used interfaces and classes of an ABAP REST service implementation:
- CL_REST_HTTP_HANDLER
This is the base handler class for ABAP REST applications, i.e. each REST application has to derive its handler class from this class and provide implementations for specific interface methods which get called during request processing.
This class comprises the interfaces IF_HTTP_EXTENSION and IF_REST_APPLICATION used by the REST framework to handle requests. The REST application’s derived handler class is the class entered in the SICF transaction as the HTTP handler. You can look at this class as the clue between the HTTP and the REST environment. - IF_HTTP_EXTENSION
This interface is part of the ICF and is used to register a handler class in the ICF. It is part of the REST handler class CL_REST_HTTP_HANDLER.
Whenever a HTTP request hits the ABAP application server, the path gets resolved by the ICF, the registered class gets instantiated and its method IF_HTTP_REQUEST~HANDLER_REQUEST is called. The class CL_REST_HTTP_HANDLER already contains the implementation for this method. - IF_REST_APPLICATION
Declares the method GET_ROOT_HANDLER, which must be implemented by any ABAP REST application to return an instance for interface IF_REST_HANDLER (see below) for handling specific REST requests. - IF_REST_HANDLER
This interface is used to call classes designed to process REST requests. For this, it declares the method HANDLE, which gets passed in the following objects from the REST framework: Request, response and context. You do not implement this interface seperatly for your REST service, but you use it via an class derived from CL_REST_RESOURCE (see below). - IF_REST_RESOURCE
This interface defines the methods GET, POST, PUT, DELETE, HEAD and OPTIONS reflecting the HTTP methods for the REST resource. It is not required to implement this interface solely by the REST application developer, but inherit it from CL_REST_RESOURCE instead. - CL_REST_RESOURCE
This class is the designated base class for all REST resources. Any REST application resource class inherits from this class and overwrites methods to implement application resource specific behavior. For each HTTP verb this base class contains a default implementation (returning HTTP 405: Not Allowed) so each method you want to support has to be overridden by your REST resource class.
This class implements the interfaces IF_REST_HANDLER and IF_REST_RESOURCE. - CL_REST_ROUTER
This class gets used to resolve REST resource handler classes from the used URIs during the REST request processing. The router is normally instantiated in the IF_REST_APPLICATION~GET_ROOT_HANDLER method of the REST application class. This method returns the handler class for a REST request implementing interface IF_REST_RESOURCE.
The selection of the handler class is based on a URI template associated with a handler class. That assignment of handler class to URI template is done by calling the ATTACH method of the rest router instance of type CL_REST_ROUTER.
All interfaces and classes, their usage and background information is available via SAP help (SAP Basis Release 7.40).
Especially the REST Programming Tutorial is of interest for people to have a first hand on ABAP REST programming.
This picture shows the sequence of class usage on the REST server side:
Example Service Implementation
The REST example used in this blog is using parts of the well-known SAP Flight sample data model. In this example, the resources are “flight” resources made available to REST clients stored in the database table SFLIGHT.
Defining the REST Application Class
The REST application class named ZCL_SCN_BLOG_FLIGHT_APP is dervied from the ABAP library class CL_REST_HTTP_HANDLER. This REST service class inherits especially the interface method IF_REST_APPLICATION~GET_ROOT_HANDLER. Within this method, at least one URI template gets attached to a REST handler class (multiple attachments are possible depending on the number of supported URIs and used classes). The handler class is named ZCL_SCN_BLOG_FLIGHT_RESOURCE in this example (for its implementation see below).
This is the implementation of method IF_REST_APPLICATION~GET_ROOT_HANDLER in the REST application class ZCL_REST_FLIGHT_APPL:
method if_rest_application~get_root_handler.
data(lo_router) = new cl_rest_router( ).
lo_router->attach( iv_template = ‘/flights’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights.{format}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}.{format}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}/{connid}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}/{connid}.{format}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}/{connid}/{fldate}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
lo_router->attach( iv_template = ‘/flights/{carrid}/{connid}/{fldate}.{format}’ iv_handler_class = ‘ZCL_SCN_BLOG_FLIGHT_RESOURCE’ ).
ro_root_handler = lo_router.
endmethod.
In this code example, multiple URL templates are attached to one and the same resource class ZCL_SCN_BLOG_FLIGHT_RESOURCE (which is not mandatory). For the details on the definition on URI templates, you may refer to the SAP documentation. In this blog only an example is given for the routing.
Example (Routing):
Assume that the URL ‘http://ldai3yi3.wdf.sap.corp:50034/sap/bc/rest/scn_test/flights/LH/0400.json?sap-client=000‘ is called with the HTTP GET method.For the correct routing of the request to the method IF_REST_RESOURCE~GET in class ZCL_SCN_BLOG_FLIGHT_RESOURCE, the class ZCL_SCN_BLOG_FLIGHT_APP has to be entered in the ICF as handler class (e.g. in the ICF node ‘SCN_SAMPLE’). The URI-attributes ‘carrid’ is set to value ‘LH’, the attribute ‘connid’ is set to value ‘400’ and the ‘format’ attribute is set to ‘json’. All these attributes get set by the REST framework and are available in the request object in the resource processor class ZCL_SCN_BLOG_FLIGHT_RESOURCE (defined as data members in its base class CL_REST_RESOURCE).
Defining the REST Resource Class
In this example, the class representing the “flight” resource is named ZCL_SCN_BLOG_FLIGHT_RESOURCE and is derived from the REST library class CL_REST_RESOURCE. It implements the methods of interface IF_REST_RESOURCE. The interface methods GET and OPTIONS are supported, i.e. this method is implemented in the resource class (note: methods not implemented will hit the default implementation of CL_REST_RESOURCE and return a HTTP 405 return code: “HTTP method ‘…’ not supported”).
This is the implementation of method IF_REST_RESOURCE~GET in class ZCL_SCN_BLOG_FLIGHT_RESOURCE:
method if_rest_resource~get.
data: lt_flight type table of sflight.
data(lo_entity) = mo_response->create_entity( ).
data(lv_carrid) = mo_request->get_uri_attribute( iv_name = ‘carrid’ ).
data(lv_connid) = mo_request->get_uri_attribute( iv_name = ‘connid’ ).
data(lv_fldate) = mo_request->get_uri_attribute( iv_name = ‘fldate’ ).
if ( lv_fldate is not initial ).
select * from sflight into table lt_flight
where carrid = lv_carrid and connid = lv_connid and fldate = lv_fldate.
elseif ( lv_connid is not initial ).
select * from sflight into table lt_flight
where carrid = lv_carrid and connid = lv_connid.
elseif ( lv_carrid is not initial ).
select * from sflight into table lt_flight
where carrid = lv_carrid.
else.
select * from sflight into table lt_flight.
endif.
data(lv_format) = mo_request->get_uri_attribute( iv_name = ‘format’ ).
if ( lv_format = ‘json’ or lv_format is initial ).
” Transform data to JSON
data(lo_json_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
call transformation id source itab = lt_flight result xml lo_json_writer.
lo_entity->set_content_type( if_rest_media_type=>gc_appl_json ).
lo_entity->set_binary_data( lo_json_writer->get_output( ) ).
elseif ( lv_format = ‘xml’ ).
” Transform data to XML
call transformation id source itab = lt_flight result xml data(lv_xml).
lo_entity->set_content_type( if_rest_media_type=>gc_appl_xml ).
lo_entity->set_binary_data( lv_xml ).
elseif ( lv_format = ‘atom’ ).
” Transform data to Atom
data: ls_feed type if_atom_types=>feed_s,
ls_entry type if_atom_types=>entry_s.
ls_feed-id-uri = ‘http://www.sap.com‘.
get time stamp field ls_feed-updated-datetime.
loop at lt_flight assigning field-symbol(<f>).
ls_entry-title-text = | { <f>-carrid }-{ <f>-connid }|.
convert date <f>-fldate
into time stamp ls_entry-updated-datetime time zone ‘UTC’.
ls_entry-title-type = if_atom_types=>gc_content_text.
append ls_entry to ls_feed-entries.
endloop.
data(lo_provider) = new cl_atom_feed_prov( ).
lo_provider->set_feed( ls_feed ).
lo_provider->write_to( lo_entity ).
endif.
mo_response->set_status( cl_rest_status_code=>gc_success_ok ).
endmethod.
Brief description of the implementation:
- A REST response entity gets created for the service’s response. This entity will carry the flight data.
- All (potentential) URI attributes are read (the names are defined in the routing class above) and set in local variables.
- Depending on the specified resource URI attributes, the data is retrieved from the data source (the database table SFLIGHT).
- The format setting attribute is used to identify the requested response format. The implementation offers three formats:
- json (default): The SXML library (with stream type json) gets used together with the ID transformation to set the content formatted in JSON.
- xml: The ID transformation is used to transfer the data into XML format.
- atom: An ATOM feed is created to return the data to the REST client.
This is the implementation of method IF_REST_RESOURCE~OPTIONS in class ZCL_SCN_BLOG_FLIGHT_RESOURCE:
method if_rest_resource~options.
mo_response->set_header_field( iv_name = if_http_header_fields=>allow iv_value = ‘OPTIONS,GET’ ).
mo_response->set_status( cl_rest_status_code=>gc_success_no_content ).
endmethod.
Brief description of the implementation:
- The OPTIONS method is generally used by a client to determine the options available with an associated resource or the capabilities of a service without calling any action or getting any resource.
- The method returns the supported HTTP verbs in HTTP header ‘Allow’, which is for this REST service GET and OPTIONS.
- The header and the status is set in the (wrapped) REST response object of the resource class.
Additional Supported Features
Beside the basic functionality of a REST service mentioned above, there are additional features integrated into the ABAP REST library (which are only mentioned in this blog without detailed explanation (for more in depth information refer to the SAP documentation):
- Content Negotiation
This is a mechanism defined in the HTTP specification that makes it possible to serve different versions of a document or different resource representations could be available with the same URI. Content negotiation is the process of selecting the best representation between client and server for a given resource when multiple representations are available. - Conditional Handling
This is an optimistic concurrency control mechanism for accessing identical data resources used by different clients. The implementation of this control is done in the environment of REST data service using by the concept of ETags (entity tags) which were introduced by the HTTP/1.1 protocol. - Multipart Requests
Beside single content per HTTP request submitted by a REST client, it is possible to pack multiple requests into one request entity, which becomes a multipart entity. When doing so a REST client saves the overhead of sending a sequence of single requests to the server (e.g. reduces network latency etc.).
Having used the Alternative Dispatch Layer by DJ Adams for some years now I am happy to see - after the SAPification of ZJSON - that yet another important connectivity framework is out-of-the-box available! Great job!
Thanks for this great introduction to the REST framework!
Hi Otto, thanks for this tutorial.
I am setting up a REST service (in a 7.4 system) in the manner you describe, by creating a node in ICF under /SAP/BC/RESOURCE and attaching a handler to to the node which inherits from class CL_REST_HTTP_HANDLER.
However, the handler expects a CSRF token as a header field for create/update methods - this token can according to documentation be obtained via a preceding GET request with the value "fetch" for the CSRF header. The problem is that it seems that the GET must supply some kind of authentication in order to obtain the CSRF token - reading the code it says "no public services allowed". Authentication via password/user on the service ("Service Logon Procedure") is not accepted.
Is there a recommneded way of either obtaining the token for a public service, or turning off the CSRF validation alltogether?
thanks!
-frank
Hi Frank,
as you already noticed, it is mandatory to have a none public service to get a CSRF token generated. This is hard coded in the CSRF token genrator called by the HTTP REST handler. There is no way in the OData or REST environment to switch this behaviour off.
- Otto
So that means, to be able to use the REST service as described above, the client will actually need to authenticate itself somehow? That seems like a severe restriction.
Is there some way to set up a public REST service (not requiring authentication) using SAP classes, or must one simply create an entirely custom handler for this?
You need a CSRF token only if your service does changes. For read operations (GET, HEAD, OPTIONS), there is no need for a CSRF token!
Understood - I was hoping to allow anonymous POSTs, just using my own generated session ID for authentication. It seems I will either have to create my own handler class or alternatively copy and modify CL_REST_HTTP_HANDLER to accomplish this.
hi..
in order to get rid of CSRF you can just:
class zcl_rest_http_handler definition public abstract inheriting from cl_rest_http_handler create public .
public section.
protected section.
methods handle_csrf_token redefinition.
private section.
endclass.
class zcl_rest_http_handler implementation.
method handle_csrf_token.
" Skip Validation..
endmethod.
endclass.
all your custom handlers should extend this z class and you will be fine.
Cheers,
D.
Is there any other way to make CSRF validation work correctly? I receive a token, but the validation (method IF_HTTP_SERVER~VALIDATE_XSRF_TOKEN) always fails...
Are you sending the token you received back in the header of your requests?
Yes, I get a token with a GET fetch and return it with my PUT request. Looks like the server has a different idea which session token should be valid?! I will try to figure this out, maybe I'm still missing something here 😕
Send me a copy og your GET (getting the token) and PUT request if you like - I've got this up on running on my system without problems
Seems to be a client problem... testing with RESTClient in Firefox works correctly.
I must be missing something in my node.js code.
Ok, it turned out that it is crucial to not only pass the CSRF token, but the session cookies as well. Works now as expected with node, unirest and Q 😎
Hi Otto,
really nice one (sorry, I'm a bit late...)
The requested content type should also be determined via "Content Negotiation" (I know, many REST APIs are using the "format" style,...), see
Content Negotiation and the REST Library - Connectivity - SAP Library
Can I install this ABAP Rest Library in BASIS 7.01?
If you have the GW_CORE component, please check the package /IWCOR/SREST.
Great blog, Kudos to you!
Hi,
When we have both the REST and ODATA available to create an API in our SAP system, which one to choose from. Can you please provide any best practice or guidelines on when to use REST and when to use ODATA.
Thanks,
Mainak
Hi Mainak,
got an automatic notice about your question. Well, this blog is over 9 years old and I do not work in the ABAP team any more. So I am not sure, if I am a good contact for your questions or if it is better to contact e.b. the ABAP Gateway team.
From the top of my head, if you need features listed in the OData specification, you should go for implementing an OData service (version 4.x preferred), which is also a REST service. If these features are not required, because it is a simple and small service you are heading for, you may go for a "custom" REST service as I described it long ago in this blog.
I even cannot answer, if everything is still the way it is described in this blog. You may also go for an ABAP Gateway service, also preferred using the V4 version there.
You may contact the ABAP Gateway team.
BR, Otto