星球里有朋友提出关于 SAP OData 的疑问。
这个疑问概括起来有两点:
- SAP OData 根据 key 值进行数据读取的正确语法是什么?
- 根据 Key 值和使用 $filter 操作,二者都能读取数据,有什么区别?
本文就来聊聊这个话题。
先说根据 Key 值进行数据读取的语法。笔者以前在 SAP CRM Fiori 应用开发团队工作时,曾经从事下面这些 Fiori Launchpad 里标准 Fiori 应用的开发工作。
后来我把开发过程中积累的经验,写了一套 SAP OData 开发实战教程:从入门到提高。
本文就用我教程里的 Book 模型作为例子。
下图是 Book 模型上的字段列表,其中 book_guid 和 book_id, 这两个字段都可以用来唯一标识一本图书。
book_guid 被设置为 Key 字段,而 book_id 对于用户来说阅读起来更友好。
很多刚接触 OData 的朋友们,在使用类型为 Edm.Guid 进行数据读取的时候,都容易犯本文开头知识星球朋友提到的 Invalid key predicate type 错误。
如果直接把类型为 Edm.Guid 的 Key 值放到 HTTP OData 请求的 Url 里,服务器端会返回 HTTP 400 Bad Request 错误。
服务器通过这个错误码提示客户端:你发过来的 HTTP 请求格式不正确。
那么正确的请求格式到底是啥?
除了网上查阅资料或者求助专家之外,我们也可以通过阅读源代码和单步调试的方式,自行找到问题答案。
登录 SAP ABAP 后台系统,使用事务码 /IWFND/ERROR_LOG, 查看这个请求对应的服务器端错误记录。
选中错误记录,点击按钮 Active Source:
系统就会自动跳转到抛出这个 HTTP 400 错误的代码位置。
我们看到系统在 IF 分支里抛出了一个异常。
这个 IF 分支的条件检查 check_compatibility 方法的返回值,如果是 false,说明检测到了问题,所以抛出异常。
那么我们就在这个 check_compatibility 方法调用的地方设置断点。
然后重新发送一次错误的 HTTP 请求,断点触发。
单步调试到方法内部。
方法内部的 Gateway 框架代码,将我们通过 HTTP Url 传入的 GUID, 识别成了普通的 STRING 类型,而不是期望的 Edm.Guid 类型。
因此这个方法返回 false.
那么就到该方法的前一个方法的源代码里去查找,弄清楚为啥 ‘42010aee-2a94-1edd-8494-c9d14e91555e’ 被当成了一个普通的字符串类型来处理?
这个方法信息量很大。
ABAP Gateway 框架,通过正则表达式,对 Url 传递的输入参数,进行类型判断。
对于 Key 类型为 Edm.Guid 的输入值,使用者必须手动加上 guid 的前缀。
同理,如果输入值为日期类型,也要加上 datetime 的前缀,以此类推。
加上 guid 前缀之后,取数请求工作正常。
下面回答第二个问题:通过 Key 值和通过 $filter 操作读取数据,二者有何区别?
从使用方法和结果来看,二者都是指定输入值,然后得到符合条件的输出。
但是从返回结果的格式看,二者还是存在细微差别。
根据 book_id 进行 $filter 操作的 HTTP Url 实例:
https://{{host}}:{{port}}/sap/opu/odata/sap/ZBOOK_MANAGE_SRV/BookSet?$filter=book_id eq ‘1001’
我把根据 Guid 和 $filter 操作读取同一本图书,返回的数据,放到一张 Slide 里比较差异。
大家请看,虽然返回的图书数据内容完全一致,但是这些数据包含在 HTTP Response 里的层级结构有差异。
通过 Guid 读取,数据包含在一个结构体里。而 $filter 操作的返回数据,包含在一个数组里。
换言之,通过 Key 值读取数据,暗含的语义是该操作会返回 0 或者 1 条数据。而 $filter 的语义是会返回 0 或者多条数据。
其实通过阅读和调试 SAP Gateway 框架源代码,也能得知二者的语义差异。
通过 Key 值读取,最后实现的方法是 get_entity, 这个方法的返回参数,是一个 ABAP Structure 类型,如下图所示:
而 $filter 操作在 get_entityset 里实现。
后缀 set 的字面意思是集合,而这个方法的输出参数,是一个 ABAP 内表,这也说明了 $filter 操作的返回数据,有可能包含多条记录。
以上就是关于本文标题问题的解答。