一,事务码SEGW
该事务码以project的概念来组织OData服务,并以project为整体来生成并发布。系统中预置了一些project可供我们参考,例如crm_opportunity,crm_task等等。
新建project,我们需要填写project名字以及所属package,还需要关注的是下图ProjectType和GenerationStrategy的可选项。
Project Type:1&2主要区别是维护注解的方式不同,4为OData4.0
Generate Strategy:1-Standard 2-for OData4.0
二,EntityType
EntityType可以理解为接口的结构,有点类似PI中配置的MessageType。右键【Entity Types】创建,或者导入(表,结构)或参考CDS
Properties:是结构的各个字段。
NavigationProperties:可选,建立Association之后会出现,用于entity之间导航。
【Name】暴露到接口的字段名,区分大小写。注意与后面的AbapFieldName用途各异
【IsKey】是否关键字
【EdmType】Edm预定义的元数据
【Precision】有效数位数;【Scale】小数位数;如123.45,有效位数5小数位数2
【MaxLength】用于限定字符型长度,abap ddic中的char和string都是对应ODataEntityType中的Edm.String
【UnitProperty】用于指定数量或金额列所参考的单位字段
【Create,Update】OData接口字段行为的限制,例如主键字段通常可创建,不可修改
【Sorted】字段是否支持$orderby操作
【Nullable】edm属性,Null可作为字段初始值,如主键字段不可空
【Filter】字段支持OData的$filter操作
【Lable】文本描述;【LableEdit】可以参考数据元素等带出文本描述
【CompType】用于嵌入下一级结构,此时EdmType就不能填了;DataModel右键--创建--CompType也是结构,不过没有Key字段指定
【ABAP Field Name】【ABAPType】Gateway内部运行时使用,为entity生成的abap数据结构是按这个名字,在后续abap编程也是用这个字段名
【Semantics】元数据的语义
<!--上面对字段的配置,形成如下接口元数据-->
<!--https://.../sap/opu/odata/sap/Y_TEST_ODATA4_SRV/$metadata-->
<Property Name="telephone" Type="Edm.String" Nullable="false" MaxLength="30" sap:label="Telephone number" sap:creatable="true" sap:updatable="true" sap:filterable="true" sap:semantics="tel" />
三,EntitySet
EntitySet都是基于EntityType的,就像表基于结构一样,EntitySet也是接口暴露出来的对象,该对象又具有增删改查等动作
【Lable】【LableEdit】【Semantics】与上一小节一样
【增,改,删】接口实体允许动作
【Pageable】分页
【Address】允许用URL直接访问对象,否则只能从父节点开始逐步导航到对象
【Search】支持OpenSearch,在Get请求中使用查询条件
【Subscription】允许订阅,在DataObject改变时会收到通知
【RequiredFilter】对象不能直接访问,必须带$filter表达式
<app:collection href="ItemSet" sap:content-version="1" sap:addressable="false" sap:pageable="false">
<atom:title type="text">ItemSet</atom:title>
<sap:member-title>Item</sap:member-title>
</app:collection>
四,生成运行时对象
在创建EntitySet时,下图Service Implementation会自动生成对应同名项目。
右键--生成运行时对象,系统会根据已定义的EntitySet生成ModelProvider类(MPC,MPC_EXT)以及DataProvider类(DPC,DPC_EXT);
MPC模型提供类将上面配置字段行为等数据对象化,帮助生成metadata。我们通常不用修改它们,改上面配置重新生成就好
DPC数据提供类中生成的方法分别对应了【Create,Delete,GetEntity,GetEntitySet,Update】;
_DPC为抽象父类,如果实体已经确定了数据源如CDS,生成方法将具有取数内容,这些方法是protected可继承重写的
_DPC_EXT为子类可以重写方法来实现我们自己的逻辑。Odata重生成不会覆盖*_EXT方法
服务对象_SRV,用于注册服务,服务路径中也能看到它 https://.../sap/opu/odata/sap/Y_TEST_ODATA4_SRV/ItemSet
模型对象_MDL,注释模型_ANNO_MDL通常来自CDS的生成
五,重写方法
不同的HTTP方法会调用不同的entity方法,GET会根据参数不同调用get_entity或get_entityset,POST会调用create_entity创建一个entity,PUT调用update_entity用于更新一个entity注意没传值的字段会被覆盖为空,PATCH会先调用get_entity查到一个entity(查不到直接报错返回)然后调用update_entity据说只会修改报文中给定的值不会出现覆盖为空值,DELETE调用delete_entity删除一个entity。
5.1 重写get_entityset
该方法是返回一个实体集的(多行),我们重写*_DPC_EXT~*_GET_ENTITYSET,默认返回xml报文,想要json用【?$format=json】
-
-
- IT_FILTER_SELECT_OPTIONS该参数用于OData的$filter方法【http://..._srv/entityset?$filter=(MATNR eq 'XA01' and QUANT le '2')】注意字段名大小写也要根据EntityType,eq/and/or/not等操作符必须小写,eq不能用=来代替,其他操作符还有【ne不等,lt小于,le小等,gt大于,ge大等】。注意IT_FILTER_SELECT_OPTIONS不支持or操作符,因为它会把条件拆成但是字段之间的or/and却没有办法表示,对于这种情况请直接用参数IV_FILTER_STRING,因为它就是URL中原始的没解析的filter条件
- IS_PAGING用于限定取数位置【http://..._srv/entityset?$inlinecount=allpages&$top=1&$skip=2】,$top取多少行,$skip跳过多少行再取数,URL后面的指令都已$开始,指令之间用&连接
-
//select * into table lt_db from ytest.
IF is_paging IS NOT INITIAL.
et_entityset = VALUE #( FOR ls_db IN lt_db FROM is_paging-skip + 1 TO is_paging-skip + is_paging-top ( docno = ls_db-docno
itmno = ls_db-itmno
matnr = ls_db-matnr
detailinfo-quant = ls_db-quant
detailinfo-unit = ls_db-unit ) ).
ENDIF.
-
-
- IT_KEY_TAB:主键值,在本方法get_entityset中这个参数好像没值,在下面get_entity取单一entity时有值
- IT_ORDER用于支持$orderby方法【http://..._srv/entityset?$orderby=ITMNO,QUANT】,默认为asc升序,可以显式为某个字段指定为降序排列【$orderby=ITMNO desc】注意中间的空格因URLcode编码要转换为【%20】
-
DATA:lt_otab TYPE abap_sortorder_tab.
lt_otab = VALUE #( for ls_order in it_order ( name = ls_order-property descending = cond #( when ls_order-order eq 'desc' then abap_true else abap_false ) ) ).
SORT et_data BY (lt_otab). //排序结果表
5.2 重写get_entity
该方法是返回单个实体(单行),在URL中指定实体的Key值就会触发这个方法【http://..._srv/entityset(DOCNO='1001',ITMNO='0020')】,每个KeyField都必须指定值(因为要唯一确定一条记录),否则服务出错。如果Key只有一个字段可以简化成【http://..._srv/entityset('1001'】
URL中指定Key值会转化为参数IT_KEY_TAB中的键值对
5.3 重写create_entity
注意只能处理一个entity,http方法使用POST,body内容可以是JSON格式也可以是XML,我们可以用get_entity得到的报文改一下
在方法中使用data_provider将报文直接反序列化为entity,然后进行常规存表等操作
DATA: ls_entity LIKE er_entity.
DATA: ls_return TYPE bapiret2,
lt_return TYPE STANDARD TABLE OF bapiret2.
io_data_provider->read_entry_data( IMPORTING es_data = ls_entity ).
"写入数据库,此外还要将这条数据返回给消费端,参考get_entity即可"
"$. Region Error Msg return
ls_return-type = 'E'.
ls_return-id = 'F2'.
ls_return-number = '001'.
ls_return-message = 'Save failed, line item may already exist'.
APPEND ls_return TO lt_return.
mo_context->get_message_container( )->add_messages_from_bapi(
it_bapi_messages = lt_return
iv_determine_leading_msg = /iwbep/if_message_container=>gcs_leading_msg_search_option-first ) .
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid = /iwbep/cx_mgw_busi_exception=>business_error
message_container = mo_context->get_message_container( ).
"$. Endregion Error Msg return
5.4 重写update_entity
该方法用于更新一个entity,更新entity的操作对应的http方法参考SEGW建模5 OData更新操作,首先URL中要指定Key值【http://..._srv/entityset(DOCNO='1001',ITMNO='0020')】,然后报文body里面包含了entity数据(与create类似的body)
5.5 重写delete_entity
URL中要指定Key值【http://..._srv/entityset(DOCNO='1001',ITMNO='0020')】,一般不需要body
六,基于RFC生成ODATA
首先创建RFC省略,在SEGW右键DataModel--Import--RFC/BORinterface,填写EntityType名,选择RFC,RFC的参数结构会带出如下图显示,选择需要生成的结构
结构导入为EntityType之后需要设定KeyField,
七,发布OData服务
进入事务码/n/IWFND/MAINT_SERVICE,点击按钮【Add Service】,我们看到如下图右侧的窗口。
首先【系统别名】填写Local,表示在本系统搜索Odata服务的实现;【技术服务名称】填写前面生成的Odata服务名;点击3所查到的OData服务会显示在4,选中4处的服务并点击按钮5,在弹出的窗口中填写开发包并保存即可。
如果勾选【联合部署】即本地部署不再有路由配置(不勾选则填系统别名),然后点击【添加所选服务】
系统别名配置:spro--SAP NetWeaver--SAP Gateway--OData Channel--Configuration--Connection Settings--SAP Gateway to SAP System--Manage SAP System Aliases
回到维护服务页面,找到刚创建的服务,Int通讯框架节点可以看到新服务在SICF中的位置,点击GatewayClient。如果要删除服务,先需要将SICF中的服务节点删掉,然后在这个维护服务页面点【删除服务】
在Gateway Client中,我们把URL后面改为$metadata可以获得接口的描述文件(类似wsdl),点击EntitySet选择后执行,可以取得接口数据。
八,查看日志
错误日志事务码:/N/IWFND/ERROR_LOG
Gateway应用日志:/N/IWFND/APPS_LOG
系统日志:SM21