SAP接口方式之HTTP请求发布Restful服务
SAP restful http 接口集中管理发布(SICF)
项目上有很多restful接口的需求,其中涉及到多个外围系统,就想着如何通过只发布一个服务,而不用通过Webservice,也不通过RFC方式,让个外围系统传入不同的报文,来决定动态的调用不同的业务处理程序。这时候就可以用SAP发布HTTP请求的方式(RESTFUL服务)来实现。
SE24继承 IF_HTTP_EXTENSION 接口,实现系统接口方法IF_HTTP_EXTENSION~HANDLE_REQUEST,再SICF配置服务几步来实现。
工作中需要发布 http 接口,以供第三方系统调用,网上有很多实现的方式都是通过 IF_HTTP_EXTENSION 接口实现的
跟外围系统定义报文格式的时候,确定了报文的总体格式,要求不管什么接口,总体格式不变。结构如下:
{
"HEAD": {
"TARGET_SYS": "SAP",
"INTF_ID": "INT_FI001",
"SOURCE_SYS": "OA"
},
"DATA":""
}
其中定义了两个固定格式,head与data,head中包含了接口的目标系统、源系统以及接口编号,data部分用于传输业务数据,结构与业务字段需根据实际确定。
注意,由于在json转abap对象时没有指定具体数据类型,所以由前端系统将所有数据转成字符串类型传输,避免在sap将小数转换成浮点数。
原始数据:
![]()
转换后数据:
![]()
确定好报文格式后,剩下来的只需要在SAP内对JSON解析时特殊处理了。
返回报文JSON串解析
METHOD if_rest_resource~post.
DATA lo_object TYPE REF TO object.
DATA lo_infter TYPE REF TO zif_interface_http.
DATA lv_clname TYPE c LENGTH 30.
DATA ls_head TYPE zsifheadinfo.
DATA ls_return TYPE bapiret2.
DATA:BEGIN OF ls_data,
head TYPE zsifheadinfo,
data TYPE REF TO data,
END OF ls_data.
" 获取报文。
DATA(lv_json) = mo_request->get_entity( )->get_string_data( ).
/ui2/cl_json=>deserialize( EXPORTING json = lv_json
CHANGING data = ls_data ).ASSIGN COMPONENT 'HEAD' OF STRUCTURE ls_data TO FIELD-SYMBOL(<fs_head>).
IF sy-subrc <> 0 OR <fs_head> IS NOT ASSIGNED OR <fs_head> IS INITIAL.
ls_return-message = '数据解析异常:缺少HEAD数据'.
return_error( ls_return ).
RETURN.
ELSE.
MOVE-CORRESPONDING <fs_head> TO ls_head.
ENDIF.ASSIGN COMPONENT 'DATA' OF STRUCTURE ls_data TO FIELD-SYMBOL(<fs_data>).
IF sy-subrc <> 0 OR <fs_data> IS NOT ASSIGNED OR <fs_data> IS INITIAL.
ls_return-message = '数据解析异常:缺少DATA数据'.
return_error( ls_return ).
RETURN.
ENDIF." 根据接口id确定业务实现类
SELECT SINGLE object_name INTO lv_clname
FROM ztiftable
WHERE intf_id = ls_head-intf_id
AND zactive = 'X'
AND zinout = 'I'.
TRY.
CREATE OBJECT lo_object TYPE (lv_clname)
EXPORTING iv_head = ls_head
iv_uuid = cl_system_uuid=>create_uuid_c36_static( ).
CATCH cx_uuid_error INTO DATA(lo_uuid_error).
ls_return-message = lo_uuid_error->get_text( ).
return_error( ls_return ).
RETURN.
CATCH cx_sy_create_object_error INTO DATA(lo_object_error).
ls_return-message = lo_object_error->get_text( ).
return_error( ls_return ).
RETURN.
CATCH cx_sy_dyn_call_illegal_method INTO DATA(lo_illegal_method).
ls_return-message = lo_illegal_method->get_text( ).
return_error( ls_return ).
RETURN.
CATCH cx_sy_dyn_call_illegal_type INTO DATA(lo_illegal_type).
ls_return-message = lo_illegal_type->get_text( ).
return_error( ls_return ).
RETURN.
CATCH cx_sy_dyn_call_param_not_found INTO DATA(lo_param_not_found).
ls_return-message = lo_param_not_found->get_text( ).
return_error( ls_return ).
RETURN.
CATCH cx_root INTO DATA(lo_root).
ls_return-message = lo_root->get_text( ).
return_error( ls_return ).
RETURN.
ENDTRY.IF lo_object IS INSTANCE OF zif_interface_http.
lo_infter ?= lo_object.
ENDIF.IF lo_infter IS INITIAL.
ls_return-message = '实例未引用接口类(ZIF_INTERFACE_HTTP)'.
return_error( ls_return ).
RETURN.
ENDIF.DATA re_data TYPE string.
DATA iv_data TYPE string.iv_data = /ui2/cl_json=>serialize( <fs_data> ).
lo_infter->procrss_data( EXPORTING iv_data = iv_data
CHANGING re_data = re_data
re_return = ls_return ).IF re_data IS NOT INITIAL.
mo_response->set_reason( 'ok' ).
mo_response->set_status( 200 ).
mo_response->create_entity( )->set_string_data( re_data ).
mo_response->create_entity( )->set_content_type( if_rest_media_type=>gc_appl_json ).
ELSE.
return_error( ls_return ).
ENDIF.
ENDMETHOD.
以上代码的大致内容就是,通过接口传入的报文,生成对应的abap内部类型,进一步将报文转换为内表或结构体,从而根据HEAD与DATA字段来将其拆分处理,HEAD为接口相关数据,DATA为业务数据。之后通过接口编号找到对应的类名,实例化出一个对应的对象,后面如果lo_object是zif_interface_http 接口的实例,则将其引用赋值给lo_infter变量,这样就可以利用lo_infter来调用zif_interface_http接口中定义的方法来进行业务处理,在处理之前将data数据执行了abap2json的转换,用于更方便传递内容。
接口 zif_interface_http 的方法如下:
INTERFACE zif_interface_httpPUBLIC.METHODS procrss_dataIMPORTING iv_data TYPE string OPTIONALiv_uuid TYPE string OPTIONALCHANGING VALUE(re_data) TYPE string OPTIONALre_return TYPE bapiret2 OPTIONAL.METHODS check_dataCHANGING VALUE(cs_return) TYPE bapiret2.METHODS save_dataCHANGING VALUE(cs_return) TYPE bapiret2.METHODS call_bapiCHANGING VALUE(cs_return) TYPE bapiret2.METHODS abap2jsonIMPORTING iv_return TYPE bapiret2RETURNING VALUE(ev_data) TYPE string.METHODS json2dataIMPORTING iv_data TYPE string OPTIONALCHANGING VALUE(re_data) TYPE data.METHODS lock_orderCHANGING VALUE(cs_return) TYPE bapiret2.
ENDINTERFACE.
配置表中的业务处理类只有一个实例构造方法,其他方法引用接口。代码如下
DATA lo_error TYPE REF TO cx_sy_dyn_call_illegal_method.TRY.zif_interface_http~json2data( EXPORTING iv_data = iv_dataCHANGING re_data = mt_data ).zif_interface_http~check_data( CHANGING cs_return = re_return ).zif_interface_http~lock_order( CHANGING cs_return = re_return ).zif_interface_http~call_bapi( CHANGING cs_return = re_return ).zif_interface_http~save_data( CHANGING cs_return = re_return ).re_data = zif_interface_http~abap2json( iv_return = re_return ).CATCH cx_sy_dyn_call_illegal_method INTO lo_error.re_return-message = lo_error->if_message~get_text( ).ENDTRY.
处理之后的响应报文由各不同的类传递出json对象,直接返回给发送方。
剩下的就是一个返回错误的方法,没什么好说的,返回一个400的报错.
HTTP公共发送接口
首选获取外围系统对应的IP或域名地址,接口编号。在此URL上给参数赋值。
FUNCTION zFIfm_http_send_com.
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" VALUE(IV_URL) TYPE STRING
*" VALUE(IV_DATA) TYPE STRING
*" VALUE(IV_APPTOKEN) TYPE STRING OPTIONAL
*" VALUE(IV_ACCESSTOKEN) TYPE STRING OPTIONAL
*" EXPORTING
*" VALUE(EV_RESPONSE) TYPE STRING
*" VALUE(ES_MESSAGE) TYPE ZSHR_MESSAGE
*"----------------------------------------------------------------------
DATA: lv_url TYPE string,
lo_http_client TYPE REF TO if_http_client,
lv_msgtx TYPE string,
lv_len TYPE i.
lv_url = iv_url.
"1检查URL ,DATA.
IF lv_url IS INITIAL.
ev_mtype = 'E'.
ev_message = 'Http请求地址不能为空'.
RETURN.
ENDIF.
IF lv_data IS INITIAL.
ev_mtype = 'E'.
ev_message = 'Http请求内容不能为空'.
RETURN.
ENDIF.
IF iv_intfid IS INITIAL.
ev_mtype = 'E'.
ev_message = '接口编号不能为空'.
RETURN.
ENDIF.
"2解析URL
CALL FUNCTION 'SWLWP_URI_PARSE'
EXPORTING
uri = lv_url
IMPORTING
host = lv_host
port = lv_port
EXCEPTIONS
uri_no_path = 1
OTHERS = 2.
IF sy-subrc <> 0.
ev_mtype = 'E'.
ev_message = '解析请求地址错误'.
RETURN.
ENDIF.
"3创建请求实例。
CALL METHOD cl_http_client=>create
EXPORTING
host = lv_host
service = lv_port
IMPORTING
client = lo_http_client
EXCEPTIONS
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
ev_mtype = 'E'.
ev_message = '创建Http请求实例错误'.
RETURN.
ENDIF.
"4 设置请求的URL地址
CALL METHOD cl_http_utility=>set_request_uri
EXPORTING
request = lo_http_client->request
uri = lv_url.
"5 设置Post方法
CALL METHOD lo_http_client->request->set_method
EXPORTING
method = if_http_entity=>co_request_method_post.
"6 Basic Authentication 认证
IF iv_auth_need IS NOT INITIAL.
SELECT SINGLE username,password INTO (@lv_username,@lv_password)
FROM ztinf WHERE intfid = @iv_intfid.
IF sy-subrc IS INITIAL.
CALL METHOD lo_http_client->authenticate
EXPORTING
username = lv_username
password = lv_password.
ELSE.
ev_mtype = 'E'.
ev_message = 'Basic Authentication 认证用户密码未配置(ZTINF)'.
RETURN.
ENDIF.
ENDIF.
" 7 设置请求内容类型
IF iv_content_type IS NOT INITIAL.
lv_content_type = iv_content_type.
CALL METHOD lo_http_client->request->set_content_type
EXPORTING
content_type = lv_content_type.
ENDIF.
"8 设置请求 header 参数
lo_http_client->request->set_header_fields( fields = it_fields ).
"9 设置请求内容
CALL METHOD lo_http_client->request->set_cdata
EXPORTING
data = lv_data.
"10 发送http 请求
CALL METHOD lo_http_client->send
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2.
IF sy-subrc <> 0.
ev_mtype = 'E'.
CASE sy-subrc.
WHEN 1.
ev_message = '发送Http请求失败,通信错误'.
WHEN 2.
ev_message = '发送Http请求失败,无效状态'.
WHEN OTHERS.
ENDCASE.
RETURN.
ENDIF.
" 11 接收返回信息
CALL METHOD lo_http_client->receive
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3.
IF sy-subrc <> 0.
ev_mtype = 'E'.
CASE sy-subrc.
WHEN 1.
ev_message = 'Http 通信失败'.
WHEN 2.
ev_message = 'Http 无效状态'.
WHEN 3.
ev_message = 'Http 处理失败'.
WHEN OTHERS.
ENDCASE.
RETURN.
ENDIF.
" 12 获取返回body
IF ev_response IS SUPPLIED.
DATA(lv_response) = lo_http_client->response->get_cdata( ).
ev_response = lv_response.
ev_mtype = 'S'.
ev_message = 'Http请求发送成功'.
DATA(lv_data_x) = lo_http_client->response->get_data( ).
ev_data = lv_data_x.
ENDIF.
CALL METHOD lo_http_client->close.
ENDFUNCTION.
应用实例1:
"调用JAVA等接口地址及URL参数赋值,多个参数赋值采用KEY,VALUE键值对方式,如
lv_service = 'http://10.0.73.149:8110/CS/api/public/hscs/ae/importJson?token=' .
lv_token = 'sourceSystem=FI&accessKey=7815696ecbf1c96e6894b779456d330e'.
lv_service = lv_service && lv_token.
lv_data = '{"sourceSystem":' && '"FI",' && '"documentNo":"111",' &&
'"eventBatchName":"FI_XXX_v1.0",' &&
'"accountingDate":"' && sy-datum(4) && '-' && sy-datum+4(2) && '-' && sy-datum+6(2) && ',' &&
'"accountingCompany":"001","reversalFlag":"N" ' &&
'"value1":" ' && ls_result-period && '",' &&
'"value1":" ' && ls_result-orgeh_id && '",' &&
'"value3":" ' && ls_result-period && '"}' . "金额
*根据URL创建一个HTTP客户端
cl_http_client=>create_by_url(
EXPORTING
url = lv_url
IMPORTING
client = lo_http_client
EXCEPTIONS
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
OTHERS = 4 ).
IF sy-subrc NE 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno INTO lv_msgtx
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
es_message-msgty = 'E'.
es_message-msgtx = lv_msgtx.
RETURN.
ENDIF.
*是否显示登陆弹出框-否
lo_http_client->propertytype_logon_popup = lo_http_client->co_disabled.
*合成header值
CALL METHOD lo_http_client->request->set_header_field
EXPORTING
name = 'Content-Type'
value = 'application/json'.
IF iv_apptoken IS NOT INITIAL.
CALL METHOD lo_http_client->request->set_header_field
EXPORTING
name = 'Authorization'
value = iv_apptoken.
ENDIF.
IF iv_accesstoken IS NOT INITIAL.
CALL METHOD lo_http_client->request->set_header_field
EXPORTING
name = 'Authorization'
value = iv_accesstoken.
ENDIF.
*传输json内容
lv_len = strlen( iv_data ) .
CALL METHOD lo_http_client->request->set_cdata
EXPORTING
data = iv_data
offset = 0
length = lv_len.
lo_http_client->send(
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2 ).
IF sy-subrc NE 0.
lo_http_client->get_last_error( IMPORTING message = lv_msgtx ).
es_message-msgty = 'E'.
es_message-msgtx = lv_msgtx.
RETURN.
ENDIF.
*获取返回内容
lo_http_client->receive(
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3 ).
IF sy-subrc NE 0.
lo_http_client->get_last_error( IMPORTING message = lv_msgtx ).
es_message-msgty = 'E'.
es_message-msgtx = lv_msgtx.
RETURN.
ENDIF.
"获取接口response返回
ev_response = lo_http_client->response->get_cdata( ).
*关闭接口
CALL METHOD lo_http_client->close.
WAIT UP TO 1 SECONDS. " 用于数据多时 防止接口返回错误
“服务没有报错时解析返回JSON串
IF ev_response NS 'Error' OR ev_response NS 'error'. " 解决服务报错 导致dump 问题
CALL FUNCTION 'ZHRFM_JSON_TO_ABAP'
EXPORTING
iv_json = lv_response_body
IMPORTING
ev_data = lt_info.
ENDIF.
ENDFUNCTION.
JSON转换成内表
METHOD json_to_data_internal."JSON ABAP"{...} object --> field, structure, ..."[...] array --> tableDATA:lv_char TYPE c,lv_token TYPE string,lv_dummy TYPE string, "is used in case of invalid fieldnamelt_dummy LIKE STANDARD TABLE OF lv_dummy, "is used in case of invalid tablelv_is_filled TYPE abap_bool,lr_line TYPE REF TO data,lr_td TYPE REF TO cl_abap_typedescr,lr_ttd TYPE REF TO cl_abap_tabledescr,lx_root TYPE REF TO cx_root.FIELD-SYMBOLS:<la_any> TYPE any,<la_line> TYPE any,<lt_any> TYPE ANY TABLE,<lt_srtd> TYPE SORTED TABLE,<lt_hash> TYPE HASHED TABLE,<lt_stnd> TYPE STANDARD TABLE.lr_td = cl_abap_typedescr=>describe_by_data( ca_data ).CASE lr_td->type_kind.WHEN cl_abap_typedescr=>typekind_dref.ASSIGN ca_data->* TO <la_any>.IF sy-subrc <> 0. ASSIGN lv_dummy TO <la_any>. ENDIF.WHEN OTHERS.ASSIGN ca_data TO <la_any>.ENDCASE.WHILE strlen( cv_json ) > 0.lv_char = cv_json(1).SHIFT cv_json BY 1 PLACES.CASE lv_char.WHEN '{'. "object starts --> next leveljson_to_data_internal(EXPORTINGiv_is_tabline = abap_falseIMPORTINGev_is_filled = ev_is_filledCHANGINGcv_json = cv_jsonca_data = <la_any> ).WHEN '['. "array starts --> next levellr_td = cl_abap_typedescr=>describe_by_data( <la_any> ).IF lr_td->kind EQ cl_abap_typedescr=>kind_table.ASSIGN <la_any> TO <lt_any>.ELSE. "Type mismatch - to prevent short dumpASSIGN lt_dummy TO <lt_any>.ENDIF.CREATE DATA lr_line LIKE LINE OF <lt_any>.ASSIGN lr_line->* TO <la_line>.CLEAR lv_is_filled.json_to_data_internal(EXPORTINGiv_is_tabline = abap_trueIMPORTINGev_is_filled = lv_is_filledCHANGINGcv_json = cv_jsonca_data = <la_line> ).IF <la_line> IS NOT INITIALOR lv_is_filled EQ abap_true.ev_is_filled = abap_true.lr_td = cl_abap_typedescr=>describe_by_data( <lt_any> ).lr_ttd ?= lr_td.IF lr_ttd->table_kind EQ cl_abap_tabledescr=>tablekind_sorted." sorted tableASSIGN <lt_any> TO <lt_srtd>.INSERT <la_line> INTO TABLE <lt_srtd>.ELSEIF lr_ttd->table_kind = cl_abap_tabledescr=>tablekind_hashed." hashed tableASSIGN <lt_any> TO <lt_hash>.INSERT <la_line> INTO TABLE <lt_hash>.ELSE." standard tableASSIGN <lt_any> TO <lt_stnd>.APPEND <la_line> TO <lt_stnd>.ENDIF.ENDIF.WHEN ']'. "Array ends --> previous levelIF lv_token IS NOT INITIAL.convert_token(EXPORTINGia_data = <la_any>CHANGINGcv_token = lv_token ).TRY.<la_any> = lv_token.ev_is_filled = abap_true.CATCH cx_root INTO lx_root. "#EC NO_HANDLERENDTRY.ENDIF.RETURN.WHEN '}'. "Object ends --> previous levelIF lv_token IS NOT INITIAL.convert_token(EXPORTINGia_data = <la_any>CHANGINGcv_token = lv_token ).TRY.<la_any> = lv_token.ev_is_filled = abap_true.CATCH cx_root INTO lx_root. "#EC NO_HANDLERENDTRY.ENDIF.RETURN.WHEN ':'. "key ends, value startsTRANSLATE lv_token TO UPPER CASE.SHIFT lv_token LEFT DELETING LEADING space.IF lv_token IS NOT INITIAL.IF lv_token EQ '$ROOT'. "Do nothing - already assigned!ELSEIF lv_token EQ '$T'. "ignore the value! do special thingsjson_to_brf_struc(CHANGINGca_data = <la_any>cv_json = cv_json ).ev_is_filled = abap_true.ELSE.ASSIGN COMPONENT lv_token OF STRUCTURE ca_data TO <la_any>.IF sy-subrc NE 0. "Field not found?ASSIGN lv_dummy TO <la_any>. "ignore the value!ENDIF.ENDIF.ENDIF.CLEAR lv_token.WHEN ','. "next value -- same levelIF lv_token IS NOT INITIAL.convert_token(EXPORTINGia_data = <la_any>CHANGINGcv_token = lv_token ).TRY.<la_any> = lv_token.ev_is_filled = abap_true.CATCH cx_root INTO lx_root. "#EC NO_HANDLERENDTRY.ENDIF.CLEAR lv_token.IF iv_is_tabline EQ abap_true. "to trigger next table lineCONCATENATE '[' cv_json INTO cv_json.RETURN.ENDIF.WHEN '"'. "string startsget_string(IMPORTINGev_string = lv_tokenCHANGINGcv_json = cv_json ).WHEN OTHERS. "ie. numbers, ...CONCATENATE lv_token lv_char INTO lv_token.ENDCASE.ENDWHILE.ENDMETHOD.
SAP 发布HTTP接口之完整的Restful ( 含Token 验证)
在工作中需要发布 http 接口,以供第三方系统调用,网上有很多实现的方式都是通过 IF_HTTP_EXTENSION 接口实现的,这次用的方式不一样。
具体过程如下:
一、代码过程
1. 使用SE24创建一个类 ZLOCAL_CL_REST,并且继承超类 CL_REST_HTTP_HANDLER 。

2. 重构方法 IF_REST_APPLICATION~GET_ROOT_HANDLER,HANDLE_CSRF_TOKEN,其中HANDLE_CSRF_TOKEN 是用于token验证的,如果不需要token验证则直接重构即可,不需要代码,需要验证的话可以保留,无需重构。

3.重构的代码,其中/GET/PO 为接口路径,ZLOCAL_CL_GET_PURCHASEORDER 为接口类

4.TOKEN 验证 如果需要进行验证的,则不需要重构方法HANDLE_CSRF_TOKEN,在原有的代码逻辑下,使用GET 方法时,在发送请求时登入了 Authentication账号密码即可正确获取token。不需要token则直接重构即可,不需要代码。
5.上述完成后,再次使用SE24 创建一个类,类名为前面的接口类 ZLOCAL_CL_GET_PURCHASEORDER .并且继承超类 CL_REST_RESOURCE。

6.继承后,根据接口的不同调用方式,重构相应的方法

这里使用GET方式进行演示
METHOD if_rest_resource~get.
*CALL METHOD SUPER->IF_REST_RESOURCE~GET
* .
DATA: lv_output_json TYPE string.
DATA: lv_ebeln TYPE ebeln.
DATA: BEGIN OF ls_out,
ebeln TYPE ebeln,
bukrs TYPE bukrs,
bsart TYPE bsart,
aedat TYPE aedat,
ernam TYPE ernam,
lifnr TYPE lifnr,
ekorg TYPE ekorg,
END OF ls_out.
DATA: BEGIN OF ls_output,
code TYPE char3,
clnt TYPE sy-mandt,
status TYPE char10,
content TYPE string,
data LIKE ls_out,
END OF ls_output.
DATA(rt_parameters) = mo_request->get_uri_query_parameters( )." 获取参数 "
LOOP AT rt_parameters ASSIGNING FIELD-SYMBOL(<fs_par>).
TRANSLATE <fs_par>-name TO UPPER CASE.
TRANSLATE <fs_par>-value TO UPPER CASE.
ENDLOOP.
ls_output-clnt = sy-mandt.
READ TABLE rt_parameters INTO DATA(ls_par) WITH KEY name = 'EBELN'.
IF sy-subrc IS INITIAL AND ls_par-value IS NOT INITIAL.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = ls_par-value
IMPORTING
output = lv_ebeln.
SELECT SINGLE * INTO CORRESPONDING FIELDS OF ls_out FROM ekko
WHERE ebeln = lv_ebeln.
IF sy-subrc IS INITIAL.
ls_output-data = ls_out.
ls_output-code = cl_rest_status_code=>gc_success_ok.
ls_output-status = 'success'.
ls_output-content = '获取成功'.
ELSE.
ls_output-data = ls_out.
ls_output-code = cl_rest_status_code=>gc_success_ok.
ls_output-status = 'fail'.
ls_output-content = '获取失败'.
ENDIF.
ELSE.
ls_output-data = ls_out.
ls_output-code = cl_rest_status_code=>gc_redirection_permanent.
ls_output-status = 'fail'.
ls_output-content = '参数错误'.
ENDIF./ui2/cl_json=>serialize(
EXPORTING
data = ls_output
compress = abap_false
pretty_name = /ui2/cl_json=>pretty_mode-camel_case
RECEIVING
r_json = lv_output_json ).
" 响应内容"
mo_response->create_entity( )->set_string_data( lv_output_json ).
" 响应内容类型 : application/json"
mo_response->create_entity( )->set_content_type( iv_media_type = if_rest_media_type=>gc_appl_json ).
" 响应状态 : 非必须"
mo_response->set_status( cl_rest_status_code=>gc_success_ok ).
" 响应说明 : 非必须"
mo_response->set_reason( cl_rest_status_code=>get_reason_phrase( cl_rest_status_code=>gc_success_ok ) ).
ENDMETHOD.
POST方式获取body.
" POST 方式传入参数,JSON"DATA(lv_input_json) = io_entity->get_string_data( ).
二、使用SICF配置服务
配置如下,处理器为 ZLOCAL_CL_REST ,即第一次创建的类。

三、Postman 测试
1、正确调用

2、如果路径不正确会提示没有合适的资源。

3、方法没启用,提示不受支持。

四、使用token
- 使用token 需要在 POST 方式调用,先对类ZLOCAL_CL_GET_PURCHASEORDER中的方法IF_REST_RESOURCE~POST 重构,为了测试这里简单重构了。
METHOD if_rest_resource~post.TYPES:BEGIN OF ts_indata ,matnr TYPE matnr,maktx TYPE maktx,END OF ts_indata.DATA: ls_data TYPE ts_indata.TYPES: BEGIN OF ts_outdata,code TYPE char3.INCLUDE TYPE ts_indata.TYPES:END OF ts_outdata.DATA: ls_outdata TYPE ts_outdata." POST 方式传入参数,JSON"DATA(lv_input_json) = io_entity->get_string_data( )." JSON 转换为内表"/ui2/cl_json=>deserialize(EXPORTINGjson = lv_input_jsonCHANGINGdata = ls_data ).CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT'EXPORTINGinput = ls_data-matnrIMPORTINGoutput = ls_data-matnrEXCEPTIONSlength_error = 1OTHERS = 2.SELECT SINGLE maktx FROM makt INTO ls_data-maktx WHERE matnr = ls_data-matnr." 内表转换JSON"DATA: lv_output_json TYPE string.MOVE-CORRESPONDING ls_data TO ls_outdata.ls_outdata-code = cl_rest_status_code=>gc_success_ok ./ui2/cl_json=>serialize(EXPORTINGdata = ls_outdatacompress = abap_falsepretty_name = /ui2/cl_json=>pretty_mode-camel_caseRECEIVINGr_json = lv_output_json )." 响应内容"mo_response->create_entity( )->set_string_data( lv_output_json )." 响应状态 : 非必须"mo_response->set_status( cl_rest_status_code=>gc_success_ok )." 响应说明 : 非必须"mo_response->set_reason( cl_rest_status_code=>get_reason_phrase( cl_rest_status_code=>gc_success_ok ) ). ENDMETHOD. -
对请求添加Basic Authentication账号密码,否则无法获取。
-
使用Postman 获取token
请求的Headers 中添加 x-csrf-token 参数,值为 fetch,即可在响应的 Headers 中取得 token 值。
测试token
1、使用错误的token会提示验证失败

2、正确的token,验证时同样需要Basic Authentication账号密码。

相关文章:
SAP接口方式之HTTP请求发布Restful服务
SAP restful http 接口集中管理发布(SICF) 项目上有很多restful接口的需求,其中涉及到多个外围系统,就想着如何通过只发布一个服务,而不用通过Webservice,也不通过RFC方式,让个外围系统传入不同的报文,来决…...
Spark-TTS:基于大模型的文本语音合成工具
GitHub:https://github.com/SparkAudio/Spark-TTS Spark-TTS是一个先进的文本到语音系统,它利用大型语言模型(LLM)的强大功能进行高度准确和自然的语音合成;旨在高效、灵活、强大地用于研究和生产用途。 一、介绍 Sp…...
Netty基础—3.基础网络协议一
大纲 1.网络基础的相关问题总结 2.七层模型和四层模型 3.物理层(网线 光缆 01电信号) 4.数据链路层(以太网协议 网卡mac地址) 5.网络层(IP协议 子网划分 路由器) 6.传输层(TCP和UDP协议 Socket 端口) 7.应用层(HTTP协议 SMTP协议) 8.浏览器请求一个域名会发生什…...
【mysql】centOS7安装mysql详细操作步骤!
【mysql】centOS7安装mysql详细操作步骤!—通过tar包方式 需要 root 权限,使用 root 用户进行命令操作。 1. 查看 CentOS 版本 cat /etc/redhat-release2. 安装rpm包,以8为例 打开 MySQL 官方 yum 仓库网站,获取与当前 CentOS …...
大白话 Vue 中的keep - alive组件,它的作用是什么?在什么场景下使用?
大白话 Vue 中的keep - alive组件,它的作用是什么?在什么场景下使用? 什么是 keep-alive 组件 在 Vue 里,keep-alive 是一个内置组件,它就像是一个“保存盒”,能把组件实例保存起来,而不是每次…...
当量子计算遇上互联网安全:挑战与革新之路
当量子计算遇上互联网安全:挑战与革新之路 量子计算,一个被誉为下一次科技革命的前沿技术,正在以惊人的速度发展。这项技术以其超越经典计算机的计算能力,为科学、医药和物流等领域带来了颠覆性变革。然而,对于互联网…...
Unity 封装一个依赖于MonoBehaviour的计时器(上) 基本功能
灵感来自下面这本书的协程部分,因此我就自己尝试写了一个 我的新书Unity3D游戏开发(第3版) | 雨松MOMO程序研究院 如果你不知道什么是协程:unity保姆级教程之协同程序_unity协同-CSDN博客 一句话概括:协程就是单线程的异步操作,其作用于Unity的主线程 1…...
使用Nodejs基于DeepSeek加chromadb实现RAG检索增强生成 本地知识库
定义 检索增强生成(RAG)的基本定义 检索增强生成(Retrieval-Augmented Generation,简称RAG)是一种结合了信息检索技术与语言生成模型的人工智能技术。RAG通过从外部知识库中检索相关信息,并将其作为提示&…...
笔试刷题专题(一)
文章目录 最小花费爬楼梯(动态规划)题解代码 数组中两个字符串的最小距离(贪心(dp))题解代码 点击消除题解代码 最小花费爬楼梯(动态规划) 题目链接 题解 1. 状态表示࿱…...
LeetCode977有序数组的平方
思路①:先平方,后快排,输出(基准元素,左小右大) 时间复杂度:O(nlogn) 思路②:双指针左右开弓,首先原数组已经是按照非递减顺序排序,那…...
React.js 基础与进阶教程
React.js 基础与进阶教程 React.js 是由 Facebook 开发的流行前端 JavaScript 库,专为构建用户界面(UI)设计,尤其适用于单页面应用(SPA)。它采用组件化开发模式,使 UI 结构更加清晰、可维护性更…...
网络变压器的主要电性参数与测试方法(4)
Hqst盈盛(华强盛)电子导读:网络变压器的主要电性参数与测试方法(4).. 今天我们继续来看看网络变压器的2个重要电性参数与它的测试方法: 1.反射损耗(Return loss&…...
【实战ES】实战 Elasticsearch:快速上手与深度实践-8.1.1基于ES的语义搜索(BERT嵌入向量)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 基于Elasticsearch与BERT的语义搜索架构设计与实战1. 传统搜索的局限性与语义搜索的崛起1.1 关键词搜索 vs 语义搜索1.2 Elasticsearch向量检索演进历程关键版本特性对比 2.…...
Windows10 WSL又又又一次崩了 Docker Desktop - Unexpected WSL error
问题:Windows10 WSL又又又一次崩了 这回报错: 然后再打开WSL Ubuntu就卡住了,等很长时间没反应,就关掉了。 手动启动Docker Desktop,报错: An unexpected error occurred while executing a WSL comman…...
XMI(XML Metadata Interchange)和XML之间的关系
XMI(XML Metadata Interchange)和XML之间的关系可以从以下几个方面进行阐述: 一、定义与背景 XML: XML(eXtensible Markup Language)是一种标记语言,被设计用来传输和存储数据。它是一种自描述…...
《深度剖析:鸿蒙系统下智能NPC与游戏剧情的深度融合》
在游戏开发领域,鸿蒙系统的崛起为开发者们带来了前所未有的机遇与挑战。尤其是在开发基于鸿蒙系统的人工智能游戏时,实现智能NPC的行为逻辑与游戏剧情紧密结合,成为了打造沉浸式游戏体验的关键。 鸿蒙系统作为一款面向全场景的分布式操作系统…...
【前端基础】:HTML
超链接标签: a href: 必须具备, 表示点击后会跳转到哪个页面. target: 打开方式. 默认是 _self. 如果是 _blank 则用新的标签页打开 <a href"http://www.baidu.com">百度</a>链接的几种形式: 外部链接: href 引用其他网站的地址 <a href"http…...
JVM垃圾收集器合集
前言:JVM GC收集器的回顾与比较 JVM(Java虚拟机)中的垃圾收集器是自动管理内存的重要机制,旨在回收不再使用的对象所占用的内存空间。以下是JVM中几种常见的垃圾收集器的详细介绍: 一、新生代垃圾收集器 1.Serial收集…...
Sourcetree——使用.gitignore忽略文件或者文件夹
一、为何需要文件忽略机制? 1.1 为什么要会略? 对于开发者而言,明智地选择忽略某些文件类型,能带来三大核心优势: 仓库纯净性:避免二进制文件、编译产物等污染代码库 安全防护:防止敏感信息&…...
unity使用mesh 画图(1)
plane 圆 空心椭圆 椭圆 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class DrawMeshManager {static DrawMeshManager instance;public static DrawMeshManager Instance {get {if (instance ! null){retu…...
本地部署 OpenManus 保姆级教程(Windows 版)
一、环境搭建 我的电脑是Windows 10版本,其他的没尝试,如果大家系统和我的不一致,请自行判断,基本上没什么大的出入啊。 openManus的Git地址:https://github.com/mannaandpoem/OpenManus 根据官网的两种安装推荐方式如…...
视频推拉流:EasyDSS平台直播通道重连转推失败原因排查与解决
视频推拉流EasyDSS视频直播点播平台,集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体,可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 用户使用EasyDSS平台对直播通道进行转推,发现只要关闭…...
机器人领域专业名词汇总
1. 电机与驱动 电机类型 DC Motor(直流电机):通过直流电源驱动的电机。Stepper Motor(步进电机):通过脉冲信号控制旋转角度的电机。Servo Motor(伺服电机):带有反馈控制的…...
成为超人 21:超人怎么学?技能的学习,如编程
成为超人 21:超人怎么学?技能的学习,如编程 ① 搞定全能自恋② 超人怎么学?③ 耐心怎么来? 宇树机器人王兴兴:奇迹也有算法,做成事没有那么难,就是把不可能三个字,拆解成…...
【科研绘图系列】python绘制分组点图(grouped dot plot)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据函数`generateRectBoxDF` 函数主要作用参数解释逻辑流程`nmfDotPlot` 函数主要作用参数解释逻辑流程画图1画图2画图3画图4介绍 【科研绘图系列】python绘制…...
Springfox、Springdoc和Swagger
Springfox、Swagger 和 Springdoc Springfox、Swagger 和 Springdoc 是用于在 Spring Boot 项目中生成API文档的工具,但它们之间有显著的区别和演进关系: 1.Swagger 简介 Swagger 是一个开源项目,旨在为 RESTful APIs 提供交互式文档。最…...
在Spring Boot项目中如何实现获取FTP远端目录结构
Java语言实现获取FTP远端目录结构的实现方式有多种,在Spring Boot 项目中,最简单和快速的方式就是使用Spring Integration 实现FTP相关的功能。 前言 本篇的示例和演示基于Windows 的FTP 服务,关于如何在Windows 开启FTP服务可以参考: Windows 如何开启和使用FTP服务 本…...
Flutter_学习记录_device_info_plus 插件获取设备信息
引入三方库device_info_plus导入头文件 import package:device_info_plus/device_info_plus.dart;获取设备信息的主要代码 DeviceInfoPlugin deviceInfoPlugin DeviceInfoPlugin(); BaseDeviceInfo deviceInfo await deviceInfoPlugin.deviceInfo;完整案例 import package…...
Java高频面试之集合-10
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:详解红黑树?HashMap为什么不用二叉树/平衡树呢? 一、红黑树(Red-Black Treeÿ…...
never_give_up
一个很有意思的题: never_give_up - Bugku CTF平台 注意到注释里面有1p.html,我们直接在源代码界面看,这样就不会跳转到它那个链接的: 然后解码可得: ";if(!$_GET[id]) {header(Location: hello.php?id1);exi…...
