ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RAP Fiori 实战:从 CDS 视图到 Fiori Elements 的完整链路与避坑指南

RAP Fiori 实战:从 CDS 视图到 Fiori Elements 的完整链路与避坑指南

简介:面向具备一定 ABAP 基础、希望系统掌握 RAP Fiori 与 ABAP RESTful 编程模型的 SAP 开发者,这份教程资料以 SAP PRESS 专业出版物内容为依托,围绕 ABAP RESTful 编程模型的实现、Fiori 应用程序开发流程以及 CDS、测试驱动开发等进阶主题展开,帮助读者理解企业级应用的构建思路与开发规范。资源包共 1 个 PDF 文件,大小约 18.31MB,内容为可检索的电子书文档,便于按章节查阅与做笔记。目前已有 366 人学习下载,具备一定的参考热度。资料覆盖从语言基础到项目规划、环境搭建、测试部署的完整链路,并涉及身份验证、授权与性能优化等实践要点,适合作为日常开发查阅手册或系统学习的参考材料。

1. 拿到一份 RAP Fiori 教程资源,先别急着翻目录

如果你手上已经有一份 RAP Fiori 相关教程资源,第一反应大概率是打开目录找“Hello World”那一章。先停一下。这份资源的核心价值不在入门示例,而在于它把 ABAP RESTful 编程模型(RAP)从 CDS 视图、行为定义、服务绑定到 Fiori Elements 预览这条链路完整串了起来。换句话说,它解决的不是“Fiori 长什么样”,而是“一个可事务化的 Fiori 应用怎么从数据模型一路长到 UI”。适合已经写过 ABAP 报表、但没做过 RAP 事务应用的人,也适合被 S/4HANA 项目推着转 Fiori 开发的老 ABAPer。资源本身是教程型文档,不是源码包,所以复现的关键在于把每一章的建模步骤落到自己的开发环境里,而不是复制粘贴。

2. RAP 的骨架:CDS 视图、行为定义与服务绑定到底怎么串

RAP 不是一个新的编程语言,它是一套在 ABAP 平台上组织事务应用的约定。理解这套约定,比记住某个注解怎么写更重要。这份教程把 RAP 拆成三层:数据层用 CDS 视图描述,行为层用行为定义(Behavior Definition)声明,服务层用服务定义和服务绑定暴露成 OData。三层各司其职,任何一层缺了,Fiori Elements 的 List Report 都跑不起来。

2.1 为什么先建 CDS 视图而不是直接写 OData

很多从 Gateway 时代过来的人习惯先建 EntitySet 再补数据。RAP 反过来:先有 CDS 视图,再有服务。原因是 Fiori Elements 的 List Report 和 Object Page 依赖 CDS 视图上的 UI 注解来生成界面,字段标签、筛选条件、对象页分区全部来自注解,而不是前端代码。教程里先让你建基础接口视图,再加计算字段,最后加数据控制语言(DCL)文件,这个顺序是有讲究的——接口视图负责稳定字段结构,投影视图负责对外暴露,DCL 负责行级权限。跳过接口视图直接建投影视图,后期改字段会牵一发动全身。

常见做法是:接口视图只放技术字段和关联,投影视图才加@UI注解。我一般会在接口视图命名上加_I后缀,投影视图加_P后缀,这样在 Eclipse 里一眼能分清哪层该改。

2.2 行为定义不是装饰,它决定事务能不能落库

教程里“生成非托管事务业务对象”那一节是整份资源的分水岭。非托管意味着保存逻辑要自己写,托管则由框架接管。选非托管通常是因为已有 BAPI 或旧逻辑要复用。行为定义里声明create、update、delete之后,必须配行为实现类,否则预览时点保存会直接报“行为未实现”。

下面这段是行为定义里最常见的骨架,教程中对应“行为定义添加事务特性”一节:

managed implementation in class zbp_travel_u unique; strict ( 2 ); define behavior for ZTRAVEL_U alias Travel persistent table ztravel lock master authorization master ( instance ) etag master LastChangedAt { create; update; delete; field ( readonly ) TravelID; field ( mandatory ) AgencyID, CustomerID, BeginDate, EndDate; mapping for ztravel { TravelID = travel_id; AgencyID = agency_id; CustomerID = customer_id; BeginDate = begin_date; EndDate = end_date; LastChangedAt = last_changed_at; } }

逻辑说明:managed implementation表示保存由 RAP 框架托管,strict ( 2 )是语法检查等级,建议新项目都开。lock master声明锁主对象,etag master指定乐观锁字段,这两个不写,并发编辑时会出现后写覆盖先写。field ( readonly )让主键在 UI 上不可编辑,mapping块把 CDS 字段名映射到数据库表字段名,字段名不一致时这是必须的,否则激活时报“字段无法分配”。

参数上最容易翻车的是etag master:它指向的字段必须在 CDS 视图里存在且类型是时间戳或哈希,用普通日期字段会报类型不匹配。另外authorization master ( instance )要求你实现权限检查,教程里在行为实现类的get_instance_authorizations方法里补,漏了这步,预览能跑但正式环境会被权限框架拦下。

2.3 服务绑定里的预览按钮,是验证链路的最短路径

服务定义声明暴露哪些实体,服务绑定选择 OData V2 还是 V4。教程里用服务绑定的“预览”功能直接拉起 Fiori Elements 应用,这是整份资源里最省时间的验证手段。预览走的是本地 Web IDE 的模拟服务,不需要部署到 Gateway,改完注解刷新即可看到 UI 变化。

操作步骤上,我一般这样走:先在服务绑定里选OData V4 - UI,绑定类型选Web API,然后点预览。如果预览报“元数据无法加载”,九成是投影视图缺了@UI.lineItem或@UI.headerInfo注解,Fiori Elements 拿不到列表列定义就会拒绝渲染。这时候回到投影视图,至少补上@UI.headerInfo: { typeName: 'Travel', typeNamePlural: 'Travels' }和主键字段的@UI.lineItem: [{ position: 10 }],再刷新。

提示:预览环境不校验 DCL 权限,别把预览通过当成权限配置完成。正式传输前必须在开发机用不同权限账号跑一遍。

3. 从 List Report 到 Object Page:注解驱动的 UI 怎么调

教程第二部分把 List Report 和 Object Page 分开讲,这个拆法对复现很友好。List Report 负责列表和筛选,Object Page 负责单条记录的明细和编辑。两者共用同一套 CDS 投影视图,区别只在注解。理解这一点,就不会在“为什么列表有字段、点进去没有”这种问题上卡半天。

3.1 列表页的筛选字段从哪来

List Report 的筛选栏不是前端配的,是 CDS 视图上@UI.selectionField注解生成的。教程里在投影视图上给AgencyID、CustomerID加@UI.selectionField: [{ position: 10 }],预览时筛选栏就出现对应输入框。如果加了注解没生效,先检查视图有没有被服务定义暴露,再检查注解是不是加在了接口视图而不是投影视图上——加错层是最常见的低级错误。

对象页的分区则靠@UI.facet控制。教程里把明细字段分成“通用信息”和“日期信息”两个 facet,每个 facet 下挂@UI.fieldGroup。这里有个参数细节:@UI.facet的purpose要设成#STANDARD,type设成#COLLECTION,否则对象页会把所有字段平铺,分区不生效。

3.2 非托管事务的保存逻辑,别在行为实现里写 SELECT

教程在“行为保存本地类”一节给了保存链路的实现位置。非托管场景下,save_modified方法里拿到的是变更后的实体,直接调 BAPI 或更新表。我见过有人在save_modified里先SELECT再判断再更新,结果在同一个 LUW 里读到旧缓冲,保存后数据对不上。正确做法是信任传入的failed和reported参数,用MODIFY ENTITIES的返回结果做判断,不要在保存方法里重新查库。

下面是一个保存方法的简化骨架,对应教程里“行为保存本地类”的实现思路:

METHOD save_modified. DATA: lt_travel_create TYPE TABLE FOR CREATE ztravel_u\\travel, lt_travel_update TYPE TABLE FOR UPDATE ztravel_u\\travel. IF create-travel IS NOT INITIAL. lt_travel_create = create-travel. " 调 BAPI 或直接 INSERT,失败时填充 failed 和 reported LOOP AT lt_travel_create INTO DATA(ls_create). INSERT ztravel FROM @( VALUE #( travel_id = ls_create-TravelID agency_id = ls_create-AgencyID customer_id = ls_create-CustomerID begin_date = ls_create-BeginDate end_date = ls_create-EndDate last_changed_at = ls_create-LastChangedAt ) ). IF sy-subrc <> 0. APPEND VALUE #( %key = ls_create-%key ) TO failed-travel. APPEND VALUE #( %key = ls_create-%key %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = '保存失败' ) ) TO reported-travel. ENDIF. ENDLOOP. ENDIF. ENDMETHOD.

逻辑说明:create-travel和update-travel是框架传入的变更集,类型是TABLE FOR CREATE/UPDATE,带%key和%cid等控制字段。失败时必须同时填failed和reported,只填failed前端会显示通用错误,只填reported框架不知道哪条失败。%key原样回传,不要自己拼主键。

参数上注意last_changed_at要在保存时更新为当前时间戳,否则 ETag 不变,下次编辑的乐观锁检查会失效。教程里在update分支里显式赋值,这一步别省。

3.3 虚拟元素和代码出口,什么时候值得用

教程在“CDS 虚拟元素”一节讲了用 ABAP 代码出口填充非数据库字段。典型场景是列表里要显示一个来自外部系统的状态,数据库里没有对应列。虚拟元素在 CDS 视图里声明virtual,然后在行为实现类的calculate方法里填充。代价是每次读列表都会触发代码出口,数据量大时性能会掉。我的经验是:虚拟元素只用于少量辅助字段,超过三个就考虑落库或用关联视图,别把代码出口当计算列用。

4. 避坑与排查:RAP 开发里最容易翻车的五件事

4.1 预览报“元数据加载失败”,但服务绑定显示已激活

现象:服务绑定激活成功,点预览后页面空白,控制台报元数据 404。原因:投影视图缺少@UI.headerInfo或主键字段没有@UI.lineItem,Fiori Elements 无法生成列表元数据。解决:在投影视图补@UI.headerInfo,并给主键加@UI.lineItem: [{ position: 10 }],重新激活服务绑定再预览。

4.2 行为实现类激活报“方法未实现”

现象:行为定义里声明了create、update,激活行为实现类时报save_modified或get_instance_authorizations未实现。原因:行为定义里的每个声明都对应实现类里的固定方法签名,少一个就激活失败。解决:用 Eclipse 的快速修复(Ctrl+1)生成缺失方法,别手写签名,TABLE FOR类型写错一个字段就编译不过。

4.3 保存后数据没落库,但前端提示成功

现象:点保存后提示“已保存”,刷新列表数据没变。原因:非托管场景下save_modified里没有真正执行INSERT/UPDATE,或者执行了但没COMMIT。RAP 框架不自动提交非托管保存。解决:在save_modified末尾显式COMMIT WORK AND WAIT,并检查sy-subrc,失败时填failed。

4.4 并发编辑时后保存的覆盖先保存的

现象:两个人同时打开同一条记录,A 先改先存,B 后改后存,A 的修改丢失。原因:行为定义里没配etag master,或者配了但保存时没更新时间戳字段。解决:行为定义加etag master LastChangedAt,保存逻辑里每次更新都把LastChangedAt设为utclong_current( ),前端会在提交时带 ETag,不匹配时框架拒绝保存。

4.5 传输后正式环境报权限错误,开发机正常

现象:开发机预览和测试都正常,传到测试环境后打开应用报“无授权”。原因:DCL 文件只配了开发账号的权限对象,或者行为实现类的get_instance_authorizations里硬编码了开发环境的权限值。解决:DCL 用aspect pfcg_auth绑定权限对象,行为实现里用AUTHORITY-CHECK做实例级检查,别在代码里写死用户或公司代码。

注意:RAP 的权限是两层,CDS 层管行级可见性,行为层管操作级授权。只配一层,另一层会在正式环境暴露。

5. 把教程变成可复用资产:我的验证清单和一条硬习惯

这份教程的价值在链路完整,但教程终究是别人的环境。要让它变成自己的东西,我一般会做三件事:第一,把教程里的示例实体换成自己业务里一个真实但简单的表,从头走一遍 CDS 接口视图、投影视图、行为定义、服务绑定、预览,全程不复制教程代码,只对照步骤;第二,在服务绑定预览通过后,立刻用两个不同权限的账号各跑一遍,确认 DCL 和行为授权都生效;第三,把行为实现类里的保存逻辑抽成一个可复用的保存类,下次新实体直接继承,不再从零写save_modified。

验证清单可以按下面这张表走,每项打勾再进下一步:

检查项通过标准常见失败点
CDS 接口视图激活无语法警告,关联可解析关联字段类型不一致
投影视图 UI 注解预览列表有列、有筛选注解加在接口视图上
行为定义激活无“方法未实现”缺get_instance_authorizations
服务绑定预览列表和对象页可打开缺@UI.headerInfo
保存落库刷新后数据存在非托管未COMMIT
并发控制后保存被 ETag 拒绝未更新时间戳字段
权限双层低权限账号看不到/改不了DCL 或行为授权缺一层

最后说一条硬习惯:从那以后我每次新建 RAP 服务绑定,预览通过的第一件事不是继续加字段,而是先用低权限账号打开一次。血泪经验是,预览环境太宽容,权限问题拖到测试环境才暴露,排查成本翻几倍。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进