【ABAP】使用 SICF 发布可配置 Restful 接口 一、前言在 SAP 系统与外部系统对接场景中RESTful 接口是数据交互的常用方案。传统接口往往需要针对性编码新增业务对接需求时改动繁琐、复用性差。本文介绍基于 SICF 实现的可配置 RESTful 接口方案。该框架搭建完成后后续开发无需修改接口底层代码只需编写对应业务 RFC 函数并在自定义配置表中维护请求 ID 与目标函数的映射关系就能快速实现数据对接与接口访问。整体方案上手简单、使用便捷能极大提升接口开发与迭代效率。二、方案介绍2.1 实现思路基于 SAP 原生 HTTP 服务 SICF 标准接口 IF_HTTP_EXTENSION 实现 RESTful 风格接口。通过自定义配置表 ZCAT004 做路由映射外部传入唯一请求标识 REQKEYID → 系统自动匹配对应 RFC 函数 → 动态调用函数并返回 JSON 结果。2.2 整体调用流程外部 POST 请求 → SICF 服务节点 → HANDLE_REQUEST 入口 → 解析 JSON 获取 REQKEYID → 查询 ZCAT004 匹配 RFC 函数 → 动态组装参数 → 动态调用函数 → 结果序列化 JSON 返回。三、配置步骤3.1 创建配置表 ZCAT004主要用于存储请求ID — RFC函数名 映射关系实现接口路由可配置化。其余字段大家也可以按照业务需求自行添加。a. 进入 SE11输入需要创建的表名 ZCAT004点击创建b. 在字段页签中添加如下字段数据元素可以按照图中的数据类型自行定义。接口唯一ID和功能模块两个字段注意不要修改后续 SICF 服务中会用到字段关键字段初始值数据元素数据类型长度简短描述MANDT√√MANDTCLNT3客户端REQKEYID√√ZE_REQKEYIDCHAR40接口唯一IDFUNCNAMERS38L_FNAMCHAR30功能模块的名称WORKFLOWIDZE_WORKFLOWIDCHAR10OA流程idTITLEZE_TITLECHAR40OA流程描述URLZE_URLCHAR255接口地址ZCRDATE7ERNAMCHAR12负责创建对象的人员姓名3.2 创建 HTTP 处理类 ZCL_HTTP实现标准接口IF_HTTP_EXTENSIONa. 创建类名称进入 SE24输入 ZCL_HTTP点击创建b. 实现接口 IF_HTTP_EXTENSIONc. 添加系统函数调用方法 CALL_FUNCTION这个方法的主要作用是接收前端传入的 JSON 请求 → 自动根据配置动态调用对应的 SAP 后台函数 → 把函数执行结果转回 JSON 返回给前端为方法定义参数方法内容直接复制可复用METHOD call_function. DATA: ptab TYPE abap_func_parmbind_tab, ptab_line TYPE abap_func_parmbind, etab TYPE abap_func_excpbind_tab, etab_line TYPE abap_func_excpbind, data_export TYPE REF TO data, data_ref TYPE REF TO data, * lt_mapping TYPE name_mappings, lv_response_json TYPE string. 接口返回参数 DATA: dyn_table TYPE REF TO data. DATA: lt_dd04l TYPE STANDARD TABLE OF dd04l. DATA:lv_error_msg TYPE bapi_msg. JSON格式化 DATA(json_data) /ui2/cl_jsongenerate( json json ).name_mappings lt_mapping ). ASSIGN json_data-* TO FIELD-SYMBOL(json_data). IF json_data IS ASSIGNED. ASSIGN COMPONENT IS_REQ OF STRUCTURE json_data TO FIELD-SYMBOL(fs_req). IF fs_req IS ASSIGNED. ASSIGN COMPONENT REQKEYID OF STRUCTURE fs_req-* TO FIELD-SYMBOL(fv_reqkeyid). IF fv_reqkeyid IS ASSIGNED. DATA(lv_reqkeyid) CONV /zyb/sappo_keyid( fv_reqkeyid-* ). CHAR 40 ENDIF. ENDIF. ENDIF. SELECT fupararef~funcname, fupararef~paramtype, fupararef~pposition, fupararef~parameter, fupararef~structure FROM fupararef AS fupararef INNER JOIN zcat004 AS zcat004 ON zcat004~funcname fupararef~funcname WHERE zcat004~reqkeyid lv_reqkeyid INTO TABLE DATA(parameters_tab). IF sy-subrc 0. code 400. reason target_function no exist. EXIT. ENDIF. TRY. 函数入参动态拼接 LOOP AT parameters_tab ASSIGNING FIELD-SYMBOL(parameter). CLEAR ptab_line. ptab_line-name parameter-parameter. ptab_line-kind COND #( WHEN parameter-paramtype E THEN abap_func_importing WHEN parameter-paramtype I THEN abap_func_exporting WHEN parameter-paramtype T THEN abap_func_tables WHEN parameter-paramtype C THEN abap_func_changing ELSE ). DATA(json_field_name) COND string( WHEN ptab_line-kind abap_func_exporting THEN IMPORT WHEN ptab_line-kind abap_func_tables THEN TABLE WHEN ptab_line-kind abap_func_changing THEN CHANGE WHEN ptab_line-kind abap_func_importing THEN EXPORT ELSE ). DATA(lv_parameter) parameter-parameter. 根据函数的入参匹配接口传入参数 ASSIGN COMPONENT lv_parameter OF STRUCTURE json_data TO FIELD-SYMBOL(parameter_val). IF sy-subrc 0 OR json_field_name EXPORT. CASE json_field_name. WHEN TABLE. 创建动态表结构 CREATE DATA dyn_table TYPE TABLE OF (parameter-structure). 创建动态内表 ASSIGN dyn_table-* TO FIELD-SYMBOL(dyn_table). GET REFERENCE OF dyn_table INTO ptab_line-value. INSERT ptab_line INTO TABLE ptab. CONTINUE. WHEN OTHERS. 动态定义承接返回参数的结构 CREATE DATA data_export TYPE (parameter-structure). ASSIGN data_export TO FIELD-SYMBOL(data_export). ptab_line-value data_export. INSERT ptab_line INTO TABLE ptab. CONTINUE. ENDCASE. ENDIF. IF json_field_name EQ TABLE. CREATE DATA data_ref TYPE TABLE OF (parameter-structure). ELSE. CREATE DATA data_ref TYPE (parameter-structure).如果是表类型参考的是结构是不是有问题呢 ENDIF. FIELD-SYMBOLS: temp TYPE any. ASSIGN parameter_val-* TO temp. IF data_ref IS BOUND. ASSIGN data_ref-* TO FIELD-SYMBOL(data_ref). ENDIF. 将传入参数按照特定格式转换 DATA(json_temp) /ui2/cl_jsonserialize( data parameter_val ). /ui2/cl_jsondeserialize( EXPORTING json json_temp CHANGING data data_ref ).name_mappings lt_mapping GET REFERENCE OF data_ref INTO ptab_line-value. INSERT ptab_line INTO TABLE ptab. ENDLOOP. CATCH cx_root. ENDTRY. etab_line-name OTHERS. etab_line-value 10. INSERT etab_line INTO TABLE etab. TRY. READ TABLE parameters_tab INTO DATA(ls_parameters_tab) INDEX 1. 动态调用函数业务逻辑部分 CALL FUNCTION ls_parameters_tab-funcname PARAMETER-TABLE ptab EXCEPTION-TABLE etab. SORT parameters_tab BY parameter. CLEAR:data_ref. UNASSIGN:data_ref. LOOP AT ptab INTO ptab_line WHERE kind abap_func_importing.只返回导出的数据 ASSIGN ptab_line-value-* TO data_ref. Serialize Data to Json DATA(lv_string) /ui2/cl_jsonserialize( data data_ref ).name_mappings lt_mapping lv_parameter ptab_line-name. IF lv_response_json IS INITIAL. lv_response_json |{ lv_parameter }:{ lv_string }|. ELSE. lv_response_json |{ lv_response_json },{ lv_parameter }:{ lv_string }|. ENDIF. ENDLOOP. lv_response_json { lv_response_json }. ev_response_json lv_response_json. code 200. CATCH cx_sy_dyn_call_param_not_found INTO DATA(lo_error_no_found). lv_error_msg lo_error_no_found-get_text( ). CATCH cx_sy_dyn_call_param_missing INTO DATA(lo_error_missing). lv_error_msg lo_error_missing-get_text( ). CATCH cx_sy_dyn_call_parameter_error INTO DATA(lo_error_parameter). lv_error_msg lo_error_parameter-get_text( ). CATCH cx_sy_dyn_call_error INTO DATA(lo_error_call). lv_error_msg lo_error_call-get_text( ). ENDTRY. ENDMETHOD.d. 编写接口的唯一入口上面创建的 call_function 是核心业务逻辑而这段handle_request 是整个 HTTP 接口的唯一入口也是SAP标准Web接口的固定入口方法。所有前端/第三方发来的HTTP请求都会先进入这个方法再转发给动态函数调用逻辑。双击 IF_HTTP_EXTENSION~HANDLE_REQUEST 方法编写代码直接复制可复用METHOD if_http_extension~handle_request. DATA: lv_string TYPE string, 传入和传出JSON格式数据所用到的变量 lv_json_req TYPE string, 传入和传出JSON格式数据所用到的变量 lv_json_res TYPE string, 传入和传出JSON格式数据所用到的变量 lv_function TYPE string. **------获取调用时候传入的参数 * CLEAR:lt_request. lv_string server-request-get_cdata( ). 获取传入的数据 JSON序列化 * DATA(json) /ui2/cl_jsonserialize( data lv_string * compress X * pretty_name /ui2/cl_jsonpretty_mode-camel_case ). * lv_function server-request-get_header_field( FUCTION_NAME ).这里的值是约定的function名称(HEADER参数) * IF lv_function IS INITIAL. * lv_function server-request-get_form_field( FUCTION_NAME ).这里的值是约定的function名称(FPRM-DATA参数) * ENDIF. * lv_json_res {code:S,MESSAGE:成功}. *———————动态调用rfc函数---------------* me-call_function( EXPORTING json lv_string IMPORTING ev_response_json lv_json_res code DATA(code) reason DATA(reason) ). *------设置返回数据格式为JSON CALL METHOD server-response-if_http_entity~set_content_type EXPORTING content_type application/json. *------设置返回数据 server-response-set_cdata( EXPORTING data lv_json_res Character data ). server-response-set_status( code code reason reason ). ENDMETHOD.3.3 发布服务a. 创建子元素输入事务代码 SICF按回车随后点击执行在 /default_host/sap/ 目录下右击 sap选择新的子元素这里我命名成 ZHTTPb. 配置处理器清单 ZCL_HTTP填写刚才创建的 ZCL_HTTP 类点击保存c. 激活服务回到菜单栏右击 ZHTTP 子元素选择激活服务四、测试服务4.1 在 ZCAT004 表添加映射规则维护 REQKEYID 和 FUNCNAME 的对应关系4.2 编写函数模块进入 SE37 编写功能函数模块这里我写了一个简单的库存查询函数 ZMMFM045 可以按照工厂、物料编码、批次、库存地点以及货位等维度查询库存并以内表形式返回。代码略。。。。4.3 使用 postman 测试这里 SAP 的鉴权方式使用 Basic Auth 的方式即可访问接口。