☰
SAP Fiori 扩展部署到 ABAP 环境的完整实践:BAS 到 gCTS 链路
2026/10/9 4:24:45 网站建设 项目流程

在 SAP 企业应用开发圈子里,Fiori 应用扩展和“把扩展后的应用发布回 ABAP 后端”这条链路,一直属于听起来简单、做起来容易翻车的组合。SAP Business Application Studio(BAS)把前端编辑、构建、预览、Git 集成都做得相当顺手,它是给云原生开发设计的工具链;真要把成果部署到 ABAP 环境,而不是 BTP Cloud Foundry,中间的门道就来了。我这些年做过不少 S/4HANA 扩展项目,也帮同行排过 BAS 部署相关的坑。这篇记录的是我基于最新项目总结出来的完整实践路径:在 BAS 中基于标准 Fiori 应用创建扩展项目、用扩展点做增量开发、再通过 gCTS 这类基于 Git 的传输通道把代码部署到 ABAP 环境,以及部署后遇到的几个典型报错的排查过程。对正在做 ABAP 环境扩展项目、或者想搞清楚 BAS 和传统 ABAP 后端交付差异的人来说,这篇应该能直接当操作手册用。

1. 扩展基于标准 Fiori 应用,为什么不能复制工程

1.1 复制的代价被反复验证

很多团队第一次拿到“扩展标准 Fiori 应用”的需求时,第一反应是把标准应用的整个工程从后端拉下来,改几个文件,再加点自己的业务逻辑。这条路在原型演示阶段确实快,但一进入正式项目就会暴露问题:标准应用作为 SAP 交付物,正常 S/4HANA 升级和 Support Package 更新时,标准代码会被覆盖,你复制出来的那一份和标准版本从此分叉,每次升级都要把差异重新比对、手工合并一次。做过两轮升级的人都知道,那不是在维护功能,那是在维护一个越来越大的补丁包。

扩展项目的正确姿势,是不动标准应用本身,而是构建一个新的 Fiori 应用,通过 manifest 里的扩展机制指向标准应用,把增量代码挂载到标准应用的扩展点上。这样标准应用升级时,扩展代码仍然能通过接口和扩展点继续工作。BAS 里所谓“扩展项目”,生成出来的正是这样一个增量应用骨架,而不是标准应用的副本。

1.2 增量扩展的机制与 Fiori 元素扩展点

Fiori 元素(Fiori Elements)在设计上就预留了扩展点。常见的几类扩展场景包括:

  • 在 List Report 列表页增加自定义列,比如补充一个标准表格里没有的业务字段展示。
  • 在 Object Page 详情页增加 Section,显示自定义内容。
  • 覆盖或扩展标准控制器的方法,在列表加载后做额外处理。
  • 通过导航配置,从标准页面跳转到完全自定义的页面。

这些扩展点最终都是通过清单文件定义的。标准应用在运行时根据 manifest 中extends字段找到扩展应用,再根据sap.ui.viewExtensions、sap.ui.controllerExtensions这样的结构去加载额外的视图片段和控制器方法。由于增量代码和标准代码被明确分开,从架构上讲,这就是 ABAP 可扩展性模型在前端层的延续。

1.3 ABAP 环境对扩展代码的接纳边界

在 ABAP 环境(代表当前主流的 ABAP Cloud 开发模型)里,SAP 交付对象是不允许直接修改的,也不允许把自定义对象放进 SAP 命名的软件组件。所以你在 BAS 里生成的扩展应用,最终也要作为一个客户开发组件,通过合适的传输通道进入 ABAP 环境,落在自定义包或独立软件组件里。这一点直接决定了后面的部署方式——你不能像标准传输请求上传那样直接把代码推进 SAP 组件,必须走客户代码自己的交付链路。

我过去见过的部署失败,一大半都是因为有人直接把扩展项目当普通 BSP 应用塞进标准包,导入时报错或者激活后找不到对象。理解了 ABAP 环境对软件组件和包归属的约束,后面部署时就能少走很多弯路。

2. BAS 中扩展工程的具体搭建步骤

2.1 模板与工程名的选择陷阱

在 BAS 中,创建扩展项目的入口是命令面板里的Fiori: Create Project from Template。需要注意,模板要选 Fiori Elements 的扩展项目模板,别选成了SAP Fiori Application从零创建模板。两者生成的结构不同,扩展项目模板会自带一套针对 Fiori 元素的扩展点骨架,从零模板则只给你一个空白工程。

工程命名上的一个常见坑是:直接沿用标准应用的 ID,或者随便用一个带中文和横杠的工程名。SAP Fiori 应用 ID 会被用作 manifest 里的组件 ID、BSP 应用名和最终 URL 的一部分,建议统一使用符合 ABAP 命名规范的大写短横杠形式,比如ZEXT_FIORI_LIST。工程名只影响本地文件夹,但组件 ID 一旦上线就很难改,提前想清楚能省不少事。

2.2 manifest.json 中关键依赖配置

扩展项目生成以后,manifest 的配置是整个增量机制的命门。核心结构大致是这样:

{ "sap.app": { "id": "ext.myfioriapp", "applicationVersion": { "version": "1.0.0" } }, "sap.ui5": { "extends": { "component": "sap.suite.ui.generic.template.ListReport", "extensions": { "sap.ui.viewExtensions": { "sap.suite.ui.generic.template.ListReport.view.ListReport": { "CustomColumn": { "className": "sap.ui.core.Fragment", "fragmentName": "ext.myfioriapp.ext.columns.CustomColumn" } } }, "sap.ui.controllerExtensions": { "sap.suite.ui.generic.template.ListReport.controller.ListReport": { "controllerName": "ext.myfioriapp.ext.controller.ListReport" } } } } } }

这里extends.component指向被扩展的基础应用组件。具体到某个标准应用时,这个值通常是标准应用的实际组件 ID,你要从标准应用的 manifest 或后端 Fiori 应用注册表里查。如果这个 ID 填错,扩展应用加载时会直接找不到基础组件,报出类似Component sap.xxx not defined的错误。

2.3 后端连接与 OData 服务绑定

扩展应用要显示数据,还是得靠后端 OData 服务。BAS 在创建项目时会让你选择数据源,如果是连接真实 ABAP 后端系统,需要在 BAS 里配置相应的目的地,或者通过 Cloud Foundry 的 Destination 服务建立连接。这一步我建议直接连真实后端,不要嫌麻烦切到本地 mock,因为很多字段、注解、导航属性只有真实服务里才有,mock 数据导出的 metadata 未必齐全。

如果是 ABAP 环境,OData 服务通常是基于 CDS 视图发布出来的,扩展应用绑定的也是这些服务。你在 manifest 的数据源配置里要核对settings.odataVersion和uri是否和后端实际发布的服务一致。重点是 URL 末尾的/和服务路径是否匹配,尤其使用 OData V4 服务时,路径少一段或最后多一个斜杠都会导致启动时请求直接 404。

2.4 本地预览与 mock 数据准备

刚生成骨架时,BAS 会生成webapp/localService目录,里面有 metadata 和 mock 数据。本地预览启动的是ui5 serve工作流,访问index.html时会自动走 local service 并伪装成后端响应,好处是开发调试快,坏处是容易让你误以为数据已经通了。

我在开发阶段会做两层验证:第一层用 mock 数据跑通 UI 交互,第二层直接把dataSource改成真实 OData 路径,重新启动预览。如果第二层能正常显示数据,说明 OData 绑定、请求头、跨域配置都没问题,再往下走部署就会顺利很多。启动本地预览时还可以在 BAS 的控制台里看到请求日志,绑定路径写错时很快就能定位。

3. 增量开发实战:自定义列、控制器扩展与自定义页面

3.1 List Report 中新增自定义列

以列表页加一列为例子。先在webapp/ext/columns下新建一个 Fragment,比如CustomColumn.xml。Fiori 元素列表页的扩展点会识别这个 Fragment,把它当成“列定义”的一部分渲染:

<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns:m="sap.m"> <m:Column hAlign="End"> <m:Label text="自定义比率" /> <m:Text text="{m>/CustomRatio}" /> </m:Column> </core:FragmentDefinition>

然后回到 manifest,把CustomColumn这个扩展点指向上面这个 Fragment。列表加载后,Fiori 元素表格会把 Fragment 里的 Column 当作一列渲染出来。这里有个细节:字段必须存在于当前 OData 服务的 metadata 中,否则{m>/CustomRatio}这类绑定只会渲染成空值,控制台也不一定报错。如果字段来自 CDS 视图但还没有暴露到服务,需要先在 ABAP 侧把字段加进视图并重新激活服务。

3.2 控制器扩展处理列联动逻辑

自定义列里的数据往往还要做一层前端加工,比如把两个字段拼接成一个展示值,或者根据某个状态改变列内文本颜色。这类逻辑不能写在 Fragment 里,要用控制器扩展。

控制器扩展文件示例:

sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/base/Log" ], function(Controller, Log) { "use strict"; return Controller.extend("ext.myfioriapp.ext.controller.ListReport", { onAfterRendering: function() { // 这里会在列表渲染完成后执行,可以做列字段格式化或初始化逻辑 Log.info("list report rendered"); } }); });

标准 Fiori 元素页面在运行时会把你的控制器方法和它自己的控制器方法合并。建议只在确定的生命周期钩子里操作,不要覆盖那些内部方法名,否则会和标准逻辑冲突。你用sap.ui.controllerExtensions扩展的不是“替换控制器”,而是“叠一层控制器”,这一点和传统 UI5 里继承 Controller 是完全不同的思路。

3.3 自定义页面和导航注册的另类思路

有些扩展需求光靠列和 Section 撑不起来,比如需要一个独立的审批面板,或者一个完全自定义的图表页面。这种场景我一般会在扩展项目里直接建一个独立视图和控制器,再注册一个导航路由,通过标准页面的 Action 按钮跳转过去。

在 manifest 的sap.ui5.routing配置里加一条路由:

"routes": [{ "pattern": "custom", "name": "CustomPage", "target": "CustomPageTarget" }], "targets": { "CustomPageTarget": { "viewType": "XML", "viewId": "CustomPage", "viewName": "ext.myfioriapp.ext.pages.CustomPage" } }

再在 List Report 的扩展控制器里通过this.getOwnerComponent().getRouter().navTo("CustomPage")完成跳转。自定义页面和标准页面可以共享同一个 OData 模型,不需要额外创建数据源。不过要留意,Fiori 元素的导航往往带有回退逻辑,自定义页面的返回按钮最好显式处理一次,别依赖浏览器的 history,否则从 Launchpad 进入时容易按出奇怪的多层回退栈。

4. 部署到 ABAP 环境的完整链路

4.1 为什么不用 BTP Cloud Foundry 那套推送流程

BAS 默认的部署目标是 BTP Cloud Foundry,很多新手在这里栽过跟头:在 BAS 里执行 Deploy,顺手就把应用推到了 Cloud Foundry,结果打开 ABAP 环境的前端页面,发现应用根本不在。原因是两条链路的目标运行时不同。

ABAP 环境里的 Fiori 应用,要回到 ABAP 自己的前端组件里,也就是 UI5 Repository 和 BSP 应用。Cloud Foundry 里的应用在 SAP BTP 的运行时上独立运行,它可以直接通过互联网访问,但和 ABAP 后端的前端框架不是一套体系。除非你做的是纯粹的 Standalone UI5 部署且所有数据都走远程服务,否则 ABAP 环境的项目老老实实走基于 Git 的交付链路。

4.2 前提条件:Git、软件组件、通信设置

部署前需要准备四样东西:

  1. 一个后端可访问的 Git 仓库,GitHub、GitLab 或者企业内部 Git 都可以。
  2. BAS 里配置好的 Git 身份,推代码时能通过认证。
  3. ABAP 环境中用于承载扩展代码的软件组件,也就是 gCTS 里说的 business 组件或逻辑组件。
  4. 合理的包结构。这个包会在 gCTS 导入时作为代码落位的地方,建议单独创建自定义命名空间的包,不要放进$TMP之类临时包。

这里有个经验:软件组件的命名尽量和你前端应用 ID、后端命名空间保持一个可追溯的对应关系。我见过ZEXT组件下堆了几十个无关对象的情况,维护和后期的权限分配都会很痛苦。

4.3 gCTS 导入与后端资源生成

假设你已经把 BAS 中的扩展项目完整提交并推送到了 Git 仓库。接下来在 ABAP 环境侧做 gCTS 导入,大致流程是:

  1. 在 gCTS 管理界面中新建仓库,绑定远程 Git 地址。
  2. 设置分支映射,一般用main分支对应开发系统。
  3. 执行导入任务,gCTS 会把远程代码拉到 ABAP 服务器,并创建对应包、包含对象。
  4. 导入完成后触发激活任务,LSMW 式的对象激活在这里会处理 UI5 静态文件的注册。
  5. 在 UI5 Repository 或 Fiori 应用注册表中检查前端应用是否生成。

这一套流程在自家 ABAP 环境里执行时,需要注意的是 gCTS 导入并不等于“上线”。导入只是把代码放到了目标系统,你还需要在 Fiori 前端服务器里把应用注册为可启动的 BSP 应用,再把 OData 服务、用户角色、Launchpad 磁贴一一配好。很多项目把 gCTS 完成误当成上线完成,最后在 Launchpad 里找不到应用,原因就在这。

4.4 部署后的激活与前端仓库检查

部署完成以后我一般会按以下顺序做检查:

检查点方法常见结果
UI5 前端仓库SE80 或 Fiori 管理工具里查看 BSP 应用应用已生成,版本号正确
OData 服务直接访问服务 metadata URL返回 XML,无 403
权限用测试账号访问应用可录入,无空白屏幕
磁贴Launchpad 配置中查找扩展应用能找到并正常打开
基础应用扩展应用和标准应用同时加载均能打开,无组件冲突

其中最容易忽略的是“测试账号权限”。ABAP 环境里前端用户不光要能访问 Launchpad,还要有对应 OData 服务的执行权限,否则应用能打开,数据请求却会静默失败或者报权限错误。这块经常要配合管理员检查 IAM、PFCG 角色以及后端通信场景配置。

4.5 备选:传统 ABAP 后端上的 abapGit 方式

如果你面对的不是独立的 ABAP 环境,而是传统 S/4HANA 系统,也可以不用 gCTS,改用 abapGit。BAS 的扩展项目本身是普通文件结构,abapGit 支持直接从 Git 仓库序列化回 ABAP 包。

思路是:在 ABAP 后端安装 abapGit,配置同一个 Git 仓库,拉取代码后会自动创建 BSP 应用的前端对象。这种方式对没有 gCTS 的传统系统很友好,但它的局限是只能处理 ABAP 和 BSP 层对象,如果扩展里还带了 CDS 视图、OData 服务或者自定义类,那类对象最好还是走标准传输请求,混用 abapGit 和 CTS 时顺序要小心,先传后端对象,再拉前端代码。顺序反了会出现前端文件引用了尚未激活的后端服务,激活直接报错。

5. 部署后运行验证与真实问题处理记录

5.1 LCHR 类型字段不允许直接在 SQL 中使用的报文

部署后我写过一个小工具,打算在 ABAP 侧校验一个 URL 记录是否已存在。程序里写了一句类似SELECT count( * ) FROM zcustom_url_store WHERE url = @iv_url,激活时直接收到这个报错:

The column "URL" cannot be used in SQL due to its type "LCHR".

这个错误的本质是字段类型问题。LCHR在 ABAP 字典里代表“长字符”类型,它的实际长度以LENG属性里隐含定义为准,和固定长度的CHAR字段不一样。ABAP SQL 的 WHERE、JOIN、GROUP BY 这类位置对字段类型有限制,部分数据库不允许直接把LCHR列当检索条件来使用。遇到这种报错,不要把它当成临时编译问题去绕,正确的处理是在数据模型层面补救。

我的做法是在表里增加一个单独的短字段保存 URL 的哈希摘要,比如url_hash CHAR(32)存 MD5 或者 SHA 的一段,再用url_hash做唯一校验和检索,URL 长文本本身只做展示。如果你是从 BAS 扩展顺带在 ABAP 侧补校验逻辑,一定要记住:前端绑定的字段和后端能够直接 SQL 检索的字段,可能是两回事。

5.2 OData 服务 403 与权限分配

扩展应用部署好后,提示应用能启动,但列表数据一直加载不出来,F12 里看到 OData 请求返回 403。这一类问题基本都出在权限模型上。

在 ABAP 环境里,OData 服务发布后,不是所有用户都有默认访问权。你要先确认服务已经被正确激活、并且分配了通信场景或 IAM 应用。很多管理员在测试阶段会直接给用户分配SAP_ALL,生产环境则严格收紧,导致测试环境一直没问题、一上生产就 403。我的建议是在部署前就做一个最小权限清单:前端访问的用户需要哪些角色、OData 服务在哪里注册、是否要开通外部通信。把这些写进部署文档,比上线后再排查强得多。

5.3 UI5 版本不匹配导致白屏或控件空白

BAS 本地开发时用的 UI5 版本通常比 ABAP 环境内置的SAP_UI组件版本新。如果 manifest 里没有明确版本约束,部署后可能直接白屏,或某些控件显示异常。

这类问题常见的表现是:

  • 应用打开后页面一片空白,F12 控制台报加载sap.ui.define失败。
  • 控件能渲染,但样式错乱,表格列挤在一起。
  • 扩展的 Fragment 用了新版本 UI5 的控件属性,在低版本环境下不生效。

处理方式分两步。第一步查看 ABAP 环境实际支持的 UI5 版本,通常可以从SAP_UI组件的版本号推算。第二步在 BAS 的ui5.yaml和 manifest 里把minUI5Version和兼容版本调低,构建时用 ABAP 环境可用的版本重新打包。如果你是做企业级扩展,强烈建议在搭建工程时就把 UI5 版本和后台组件版本对齐,不要等部署后才调。

5.4 Fiori Launchpad 里找不到扩展后的应用

最后一个常见问题是:扩展应用的代码已经在系统里了,但 Launchpad 里看不到。

大多数情况下,扩展应用是一个独立的应用,它和标准应用是两个不同的注册项。你在 Launchpad 配磁贴时,如果只配置标准应用,那用户进入的还是标准界面;扩展应用的磁贴必须额外添加。也有一种情况是扩展应用使用了和标准应用相同的地点和组,但它需要重新绑定到角色里才会出现在用户桌面上。

我这里给一个排查顺序:先在 Fiori 应用注册表里确认扩展应用存在;再检查 Launchpad 配置里是否有对应的目标映射和磁贴;最后检查用户角色是否包含这个磁贴。一层一层查下来,基本都能定位到具体环节。

这个项目跑完以后,我最大的感受是:SAP BAS 的扩展开发本身并不复杂,复杂的是前后端两套交付体系的衔接。标准 Fiori 应用的扩展点给开发者留了足够的空间,但空间越大,越要约束好自己——不要复制标准工程、不要在自定义包里塞无关对象、不要绕过 Git 直接手工维护前端文件。把这几点管住,BAS 到 ABAP 环境的部署链路就会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询