30分钟用Restful ABAP Programming模型开发一个支持增删改查的Fiori应用

简介: 30分钟用Restful ABAP Programming模型开发一个支持增删改查的Fiori应用

2016年时,Jerry曾经写过一系列关于SAP Fiori Smart Template(现在更名为Fiori Elements了)的博客,介绍了所谓的MDD开发方法论 - Metadata Driven Development,即通过开发维护了对应annotation(注解)的CDS view,结合SAP WebIDE,能够花费最少的编程代价,就能够在短时间内获得一个支持增删改查的Fiori应用。


这个系列的博客集可以在Jerry这篇公众号文章里获得:Jerry的通过CDS view + Smart Template 开发Fiori应用的blog合集。


三年的时间过去了,ABAP在不断向前进化,如今我们有了新的编程模型:Restful ABAP Programming模型,简称为RAP模型。该模型定义了一套架构体系,应用开发人员能够凭借其来高效地进行应用的端到端开发,这种应用具有与生俱来的Restful特质,能充分利用HANA平台的强大计算能力,支持云环境和Fiori UX。

image.png

RAP模型的三大支柱:

  • Business Service
  • Core Data Service
  • Behavior Definition
  • image.png下面请跟着Jerry一起,通过一个实际的例子,了解一下这种全新的通过Restful ABAP Programming模型进行Fiori应用开发的步骤吧。


Jerry还是沿用传统ABAP On-Premises编程培训教材里使用过的经典的SFLIGHT模型来作为底层数据库存储。


(1)首先创建一个数据库表ZTRAVEL_JERRY:(如果想复制这段源代码,请点击文末的“阅读原文”获得)

@EndUserText.label : 'Database table for travel data XXX'

@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE

@AbapCatalog.tableCategory : #TRANSPARENT

@AbapCatalog.deliveryClass : #A

@AbapCatalog.dataMaintenance : #LIMITED

define table ztravel_jerry {

 key client      : abap.clnt not null;

 key travel_id   : /dmo/travel_id not null;

 agency_id       : /dmo/agency_id;

 customer_id     : /dmo/customer_id;

 begin_date      : /dmo/begin_date;

 end_date        : /dmo/end_date;

 @Semantics.amount.currencyCode : 'ztravel_jerry.currency_code'

 booking_fee     : /dmo/booking_fee;

 @Semantics.amount.currencyCode : 'ztravel_jerry.currency_code'

 total_price     : /dmo/total_price;

 currency_code   : /dmo/currency_code;

 description     : /dmo/description;

 created_by      : syuname;

 created_at      : timestampl;

 last_changed_by : syuname;

 last_changed_at : timestampl;

}因为我们在ABAP Development Tools里无法用事务码SE16手动往这张表里插入数据,所以我创建一个ABAP类,用ABAP代码往这个表里插入三条数据。

image.png

按F9执行这个ABAP类,然后看到三条数据成功插入了:

image.png(2) 我们最终的目的是创建一个支持对这张表进行增删改查的Fiori应用,而Restful ABAP Programming模型的三大支柱之一为Core Data Service,因此我们首先得有基于数据库表ZTRAVEL_JERRY的CDS view.


所以我首先创建一个CDS view:

@AbapCatalog.sqlViewName: 'ZVI_TRAVEL'

@AbapCatalog.compiler.compareFilter: true

@AbapCatalog.preserveKey: true

@AccessControl.authorizationCheck: #CHECK

@EndUserText.label: 'Travel data - XXX'

define root view ZI_TRAVEL_JERRY

as select from ztravel_jerry as Travel

/* Associations */

association [0..1] to /DMO/I_Agency   as _Agency   on $projection.agency_id = _Agency.AgencyID

association [0..1] to /DMO/I_Customer as _Customer on $projection.customer_id = _Customer.CustomerID

association [0..1] to I_Currency      as _Currency on $projection.currency_code = _Currency.Currency

{

 key travel_id,

    agency_id,

    customer_id,

    begin_date,

    end_date,

    @Semantics.amount.currencyCode: 'currency_code'

    booking_fee,

    @Semantics.amount.currencyCode: 'currency_code'

    total_price,

    @Semantics.currencyCode: true

    currency_code,

    description,

/*-- Admin data --*/

    @Semantics.user.createdBy: true

    created_by,

    @Semantics.systemDateTime.createdAt: true

    created_at,

    @Semantics.user.lastChangedBy: true

    last_changed_by,

    @Semantics.systemDateTime.lastChangedAt: true

    last_changed_at,

    /* Public associations */

    _Agency,

    _Customer,

    _Currency

}        image.png

然后创建一个projection view,将该view的字段有选择性地暴露出来。

@EndUserText.label: 'Travel projection view - Processor'

@AccessControl.authorizationCheck: #NOT_REQUIRED

@UI: {

headerInfo: { typeName: 'Travel', typeNamePlural: 'Travels', title: { type: #STANDARD, value: 'TravelID' } } }

@Search.searchable: true

define root view entity ZC_TRAVEL_JERRY as projection on ZI_TRAVEL_JERRY {

@UI.facet: [ { id:              'Travel',

               purpose:         #STANDARD,

               type:            #IDENTIFICATION_REFERENCE,

               label:           'Travel',

               position:        10 } ]

@UI: {

    lineItem:       [ { position: 10, importance: #HIGH } ],

    identification: [ { position: 10, label: 'Travel ID [1,...,99999999]' } ] }

@Search.defaultSearchElement: true

key travel_id          as TravelID,

@UI: {

    lineItem:       [ { position: 20, importance: #HIGH } ],

    identification: [ { position: 20 } ],

    selectionField: [ { position: 20 } ] }

@Consumption.valueHelpDefinition: [{ entity : {name: '/DMO/I_Agency', element: 'AgencyID'  } }]

@ObjectModel.text.element: ['AgencyName'] ----meaning?

@Search.defaultSearchElement: true

agency_id          as AgencyID, _Agency.Name       as AgencyName,

@UI: {

    lineItem:       [ { position: 30, importance: #HIGH } ],

    identification: [ { position: 30 } ],

    selectionField: [ { position: 30 } ] }

@Consumption.valueHelpDefinition: [{ entity : {name: '/DMO/I_Customer', element: 'CustomerID'  } }]

@ObjectModel.text.element: ['CustomerName']

@Search.defaultSearchElement: true

customer_id        as CustomerID,

@UI.hidden: true

_Customer.LastName as CustomerName,

@UI: {

    lineItem:       [ { position: 40, importance: #MEDIUM } ],

    identification: [ { position: 40 } ] }

begin_date         as BeginDate,

@UI: {

    lineItem:       [ { position: 41, importance: #MEDIUM } ],

    identification: [ { position: 41 } ] }

end_date           as EndDate,

@UI: {

    lineItem:       [ { position: 50, importance: #MEDIUM } ],

    identification: [ { position: 50, label: 'Total Price' } ] }

@Semantics.amount.currencyCode: 'CurrencyCode'

total_price        as TotalPrice,

@Consumption.valueHelpDefinition: [{entity: {name: 'I_Currency', element: 'Currency' }}]

currency_code      as CurrencyCode,

@UI.identification: [ { position: 60, label: 'Remarks' } ]

description as Description,

@UI.hidden: true

last_changed_at    as LastChangedAt

}        

大家可以注意到,这个projection view里包含了很多@UI注解,作用和Fiori Elements一样,作为元数据,告诉对应的渲染框架,运行时这些字段应该以什么样的方式渲染在Fiori UI上。

image.png

(3) 现在三大支柱之一的Core Data Service已经就位了,接下来我们基于前一步得到的projection view创建Business Service. 选中projection view,右键选择New Service Definition:

image.png

这个服务定义的第一条记录,就是通过ABAP expose关键字把projection view ZC_TRAVEL_JERRY暴露出来,模型名称为TravelProcessor:

image.pngimage.png

然后基于这个Service Definition创建一个Service Binding,可以简单把Service Binding理解成Service Definition的一个实例:

image.pngimage.png

Service Binding创建完毕后,点击Activate激活:

image.png

之前Service Definition里用expose关键字暴露并指定成的模型TravelProcessor此时就可见了,双击:

image.png

双击后会自动打开一个链接,一个Fiori应用就呈现在我们眼前了。我们没有进行一行的JavaScript web编程,就得到了一个专业的支持高级搜索的Fiori应用,能查看底层数据库表ZTRAVEL_JERRY的内容。

image.png

(4) 至此我们已经了解了Restful ABAP Programming模型的前两大支柱,还剩下Behavior Definition. 既然RAP的口号是打造具有Restful特性的应用,但到目前为止我们还没有感受到RAP对Restful的支持,这有待Behavior Definition来完成。


选中之前创建的CDS view,创建一个新的Behavior Definition:


image.pngimage.png


image.png

实现类型指定为Managed:

image.png

我们可以看到这个Behavior Definition的定义里,又多了一些新的ABAP关键字。这个Behavior Definition负责定义底层模型的Transaction Behavior,即代码第18到20行的create,update,delete.

image.png

当然增删改查的功能光定义不行,还得创建其对应的实现。上图Definition中已经指定了实现这些行为的ABAP类名称为ZCL_BP_I_TRAVEL_M_JERRY. 为此,右键选择New Behavior Implementation:

image.png

创建这个特殊的ABAP实现类:

image.png

这个实现类里面也不需要开发人员手动编写代码来完成对底层数据库表的增删改查操作——既然能称之为一个编程模型,那么这些通用的功能都通过框架类CL_ABAP_BEHAVIOR_HANDLER统一完成了,应用开发人员只需要定义一个对该类的声明即可。

image.pngimage.png

把这一步创建好的Behavior Definition模型和其实现全部激活,然后回到我们之前浏览器里打开的Fiori应用,刷新,会发现多了Create和Delete两个按钮,这意味着该应用对创建和删除的支持也已经自动可用了。

image.png

同之前的搜索功能一样,这些功能的自动获得,都是建立在应用开发人员一行JavaScript代码都不用编写的基础上的,由此大家感受到了Restful ABAP Programming模型的强大威力了吗?


后续Jerry会继续介绍如何给这个Fiori应用底层使用的模型增添Action和Validation功能,敬请期待。


image.pngimage.pngimage.png

image.png

相关文章
|
5天前
|
缓存 监控 API
构建高效可扩展的RESTful API:后端开发的实践指南
【4月更文挑战第26天】在现代Web开发中,构建一个高效、可扩展且易于维护的RESTful API是后端工程师必须面对的挑战。本文将深入探讨如何利用最佳实践和流行技术,设计出符合REST架构原则的服务端接口。我们将重点讨论API版本控制、资源路由、数据库优化、缓存策略以及安全性考虑等方面,旨在为开发者提供一套综合性解决方案,帮助其提升API的性能与可靠性。
|
2月前
|
缓存 安全 测试技术
构建高效的RESTful API:后端开发的实践指南
【2月更文挑战第17天】在数字化转型的浪潮中,RESTful API已成为连接不同软件组件、实现数据交互的核心桥梁。本文将深入探讨如何构建一个高效、可扩展且安全的RESTful API,涉及设计原则、开发流程以及性能优化等关键方面。我们将透过实际案例,展示如何在保证简洁性和灵活性的同时,满足日益增长的业务需求和技术挑战。
|
1天前
|
存储 关系型数据库 Go
【Go语言专栏】基于Go语言的RESTful API开发
【4月更文挑战第30天】本文介绍了使用Go语言开发RESTful API的方法,涵盖了路由、请求处理、数据存储和测试关键点。RESTful API基于HTTP协议,无状态且使用标准方法表示操作。在Go中,通过第三方库如`gorilla/mux`进行路由映射,使用`net/http`处理请求,与数据库交互可选ORM库`gorm`,测试则依赖于Go内置的`testing`框架。Go的简洁性和并发性使得它成为构建高效API的理想选择。
|
1天前
|
JSON API 数据处理
【Swift开发专栏】Swift中的RESTful API集成实战
【4月更文挑战第30天】本文探讨了在Swift中集成RESTful API的方法,涉及RESTful API的基础概念,如HTTP方法和设计原则,以及Swift的网络请求技术,如`URLSession`、`Alamofire`和`SwiftyJSON`。此外,还强调了数据处理、错误管理和异步操作的重要性。通过合理利用这些工具和策略,开发者能实现高效、稳定的API集成,提升应用性能和用户体验。
|
1天前
|
机器学习/深度学习 算法 安全
深度学习在图像识别中的应用与挑战构建高效可扩展的RESTful API:后端开发的实战指南
【4月更文挑战第30天】 随着计算机视觉技术的飞速发展,深度学习在图像识别领域取得了显著的成果。本文将探讨深度学习技术在图像识别中的应用及其所面临的挑战。首先,我们将介绍深度学习的基本原理和关键技术,然后分析其在图像识别中的优势和应用案例。最后,我们将讨论当前深度学习在图像识别领域所面临的主要挑战和未来的发展趋势。
|
2天前
|
XML JSON API
【PHP开发专栏】PHP RESTful API设计与开发
【4月更文挑战第29天】本文探讨了在Web开发中流行的前后端分离模式,重点介绍了RESTful API的设计与实现。REST是一种基于HTTP协议的架构风格,核心概念包括资源、表述和状态转换。RESTful API设计遵循无状态、统一接口等原则,使用GET、POST、PUT、DELETE等HTTP方法执行操作,并通过状态码和JSON/XML传输数据。在PHP中实现RESTful API,可通过定义路由、创建控制器、处理请求和响应,同时注意安全性措施,如使用HTTPS。文中还提供了一个用户管理API的实战示例,以帮助读者更好地理解和应用RESTful API。
|
15天前
|
JSON 安全 API
Flask-Login与Flask-RESTful:扩展你的应用功能
【4月更文挑战第16天】本文介绍了两个实用的Flask扩展——Flask-Login和Flask-RESTful。Flask-Login提供用户认证和会话管理,简化了登录、注销和保护路由的逻辑。而Flask-RESTful则助力构建RESTful API,支持多种HTTP方法和请求解析。通过这两个扩展,开发者能轻松增强Flask应用的功能性,实现安全的用户认证和高效的API交互。
|
17天前
|
缓存 负载均衡 NoSQL
构建高效可扩展的RESTful API:后端开发的最佳实践
【4月更文挑战第14天】在当今快速发展的网络应用时代,一个结构良好且高效的RESTful API是确保后端服务可靠性和性能的关键。本文深入探讨了设计和实现高效可扩展RESTful API的最佳实践,涵盖了API设计原则、数据库优化、缓存策略以及负载均衡等关键方面。文中不仅提供了具体的技术建议,还通过案例分析展示了如何应对实际开发中的挑战。
|
19天前
|
小程序 前端开发 API
小程序全栈开发中的RESTful API设计
【4月更文挑战第12天】本文探讨了小程序全栈开发中的RESTful API设计,旨在帮助开发者理解和掌握相关技术。RESTful API基于REST架构风格,利用HTTP协议进行数据交互,遵循URI、客户端-服务器架构、无状态通信、标准HTTP方法和资源表述等原则。在小程序开发中,通过资源建模、设计API接口、定义资源表述及实现接口,实现前后端高效分离,提升开发效率和代码质量。小程序前端利用微信API与后端交互,确保数据流通。掌握这些实践将优化小程序全栈开发。
|
24天前
关于 SAP ABAP OData 服务如何实现 Deep Insert 场景 - SAP 应用的标准行为试读版
关于 SAP ABAP OData 服务如何实现 Deep Insert 场景 - SAP 应用的标准行为试读版
16 1