OData Project 없이 REST API 개발
외부 서비스 통신용 REST API
SAP에서는 Gateway Service Builder(SEGW)를 이용하여 간단하게 REST API의 개발이 가능합니다.
SAP OData를 통해 제공되는 방식으로 Fiori와 같은 SAP서비스에 사용할때는 충분하지만,
외부 서비스(네이버, 카카오 등등)와 REST 통신시에는 지저분한 구조(__metadata, "d":{ "results":{ 등)와 동적구조를 만들기 어려워
일반적인 WEB API처럼 자유롭게 JSON 처리를 하고자 여러 시도를 했습니다.
1) Enhancement를 이용하여 불필요한 metadata를 제거하여 갈끔한 JSON 처리
2) 중계서버를 이용하여 OData Entity의 단일 필드에 Base64 Encoded JSON을 넣어서 중계서버를 통한 동적 JSON 처리
OData 구조적 한계의 해결을 위해 SAP Blog에서 REST Library와 ICF 서비스를 이용하여 OData Project 없이 자유롭게 REST API를 개발할 수 있는 방법이 있어서 예시소스와 함께 간단히 정리하였습니다.
#1. ICF 서비스에 사용할 Handler Class 생성
Route map 처리를 위한 Class로서, OData 기준으로는 Project 또는 Entityset 단위로 생성하여 URI에 대해 해당 기능이 구현된 Class를 호출 할수 있도록 기능 구현
1) SE24에서 클래스 생성
2) Properties > Superclass 에 "CL_REST_HTTP_HANDLER" 입력 후 저장
3) IF_REST_APPLICATION~GET_ROOT_HANDLER 메소드 Redefine
4) URI별로 기능이 구현된 Class를 호출할 수 있도록 경로 설정
"Route 설정
IF MO_SERVER IS NOT BOUND.
EXIT.
ENDIF.
DATA(LO_ROUTER) = NEW CL_REST_ROUTER( ).
"OData Project 단위처럼 handler를 만들경우, entityset별로 routemap 설정
LO_ROUTER->ATTACH( IV_TEMPLATE = '/materialinfo' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
LO_ROUTER->ATTACH( IV_TEMPLATE = '/materialinfo/{matnr}' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
LO_ROUTER->ATTACH( IV_TEMPLATE = '/materialinfo/{matnr}/{werks}' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
* "OData Entityset 단위처럼 handler를 만들 경우, 파라미터만 routemap에 설정 (sicf에 해당 단위별로 서비스 생성및 handler 지정)
* LO_ROUTER->ATTACH( IV_TEMPLATE = '' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
* LO_ROUTER->ATTACH( IV_TEMPLATE = '/{matnr}' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
* LO_ROUTER->ATTACH( IV_TEMPLATE = '/{matnr}/{werks}' IV_HANDLER_CLASS = 'ZCL_REST_MM_MATERIAL' ).
RO_ROOT_HANDLER = LO_ROUTER.
5) CSRF Token 없이 POST/PUT/DELETE를 사용하려면 "HANDLE_CSRF_TOKEN"를 redefine 하여 CSRF Token을 점검하기 않게 수정하면 됩니다
#2. REST 처리를 위한 Class 생성
Handler class에 IV_HANDLER_CLASS에 지정한 각각의 Class를 생성하여 기능 구현
1) Superclass에 "CL_REST_RESOURCE" 지정하여 클래스 생성
2) 사용하고자 하는 Method를 redefine하여 기능 구현
HEAD, GET, DELETE, OPTIONS, POST, PUT 메소드 사용 가능
3) GET 예시
method IF_REST_RESOURCE~GET.
*SUPER->IF_REST_RESOURCE~GET( ).
* JSON
TYPES: BEGIN OF T_ITEM,
WERKS TYPE MARC-WERKS,
WERKS_T TYPE T001W-NAME1,
END OF T_ITEM,
TT_ITEM TYPE STANDARD TABLE OF T_ITEM WITH EMPTY KEY.
TYPES: BEGIN OF T_OUTPUT,
MATNR TYPE MARA-MATNR,
MAKTX TYPE MAKT-MAKTX,
ITEMS TYPE TT_ITEM,
END OF T_OUTPUT.
DATA LS_OUTPUT TYPE T_OUTPUT.
GET TIME STAMP FIELD DATA(LV_CURRENT_TIME).
* Request Parameters
DATA(LV_MATNR) = MO_REQUEST->GET_URI_ATTRIBUTE( IV_NAME = 'matnr' ).
DATA(LV_WERKS) = MO_REQUEST->GET_URI_ATTRIBUTE( IV_NAME = 'werks' ).
* Processing
LS_OUTPUT = VALUE #( MATNR = LV_MATNR MAKTX = '자재'
ITEMS = VALUE #( ( WERKS = LV_WERKS WERKS_T = '플랜트1' )
( WERKS = '2000' WERKS_T = '플랜트2' ) )
).
* Accept
DATA(LV_ACCEPT) = TO_UPPER( MO_REQUEST->GET_HEADER_FIELD( 'Accept' ) ).
DATA(LV_CONTENT_T) = TO_UPPER( MO_REQUEST->GET_HEADER_FIELD( 'Content-Type' ) ).
* Response
DATA(LO_ENTITY) = MO_RESPONSE->CREATE_ENTITY( ).
LO_ENTITY->SET_MODIFICATION_DATE( IV_MODIFICATION_DATE = LV_CURRENT_TIME ).
LO_ENTITY->SET_CONTENT_COMPRESSION( ABAP_TRUE ).
IF LV_ACCEPT IS INITIAL OR LV_ACCEPT CS TO_UPPER( IF_REST_MEDIA_TYPE=>GC_APPL_JSON ).
"Transform data to JSON
LO_ENTITY->SET_CONTENT_TYPE( IF_REST_MEDIA_TYPE=>GC_APPL_JSON ).
* DATA(LO_JSON_WRITER) = CL_SXML_STRING_WRITER=>CREATE( TYPE = IF_SXML=>CO_XT_JSON ).
* CALL TRANSFORMATION ID SOURCE RESULT = LS_OUTPUT RESULT XML LO_JSON_WRITER.
* LO_ENTITY->SET_BINARY_DATA( LO_JSON_WRITER->GET_OUTPUT( ) ).
LO_ENTITY->SET_BINARY_DATA( CL_BCS_CONVERT=>STRING_TO_XSTRING( /UI2/CL_JSON=>SERIALIZE(
EXPORTING
DATA = LS_OUTPUT
PRETTY_NAME = /UI2/CL_JSON=>PRETTY_MODE-CAMEL_CASE
) ) ).
ELSEIF LV_ACCEPT CS TO_UPPER( IF_REST_MEDIA_TYPE=>GC_APPL_XML ).
"Transform data to XML
LO_ENTITY->SET_CONTENT_TYPE( IF_REST_MEDIA_TYPE=>GC_APPL_XML ).
CALL TRANSFORMATION ID SOURCE RESULT = LS_OUTPUT RESULT XML DATA(LV_XML).
LO_ENTITY->SET_BINARY_DATA( LV_XML ).
ELSE.
* error occured
LO_ENTITY->SET_STRING_DATA( 'Error occured' ).
LO_ENTITY->SET_CONTENT_TYPE( IV_MEDIA_TYPE = 'text/plain' ).
MO_RESPONSE->SET_STATUS( CL_REST_STATUS_CODE=>GC_CLIENT_ERROR_BAD_REQUEST ).
EXIT.
ENDIF.
* finalize the output
MO_RESPONSE->SET_STATUS( CL_REST_STATUS_CODE=>GC_SUCCESS_OK ).
endmethod.
4) HEAD 예시
method IF_REST_RESOURCE~HEAD.
*SUPER->IF_REST_RESOURCE~HEAD( ).
ME->IF_REST_RESOURCE~GET( ).
endmethod.
5) OPTIONS 예시
method IF_REST_RESOURCE~OPTIONS.
*SUPER->IF_REST_RESOURCE~OPTIONS( ).
MO_RESPONSE->SET_HEADER_FIELD( IV_NAME = IF_HTTP_HEADER_FIELDS=>ALLOW
IV_VALUE = 'OPTIONS,GET,POST,HEAD' ).
MO_RESPONSE->SET_STATUS( CL_REST_STATUS_CODE=>GC_SUCCESS_NO_CONTENT ).
endmethod.
#3. ICF 서비스 생성
서비스 생성시에는 계층형 구조로 생성하여 '시스템' 또는 '모듈'별로 기능을 구분할 수 있도록 하는게 좋습니다
1) SICF 에서 서비스 생성
2) 서비스에 Handler class 지정
3) 서비스 호출 테스트
참고 : https://blogs.sap.com/2013/05/16/usage-of-the-abap-rest-library-sapbasis-740/
Usage of the ABAP REST Library [SAP_BASIS 7.40] | SAP Blogs
20 42 56,468 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
blogs.sap.com
'ABAP > OData' 카테고리의 다른 글
[OData] ABAP내에서 OData 호출 방법 (0) | 2023.11.21 |
---|---|
[OData] CSRF 인증 비활성화 방법 (0) | 2023.10.31 |
[OData] CSRF Token 받기 (0) | 2023.10.31 |
[OData] OData 개념 및 T-Code (0) | 2023.10.31 |
댓글