SAPUI5 (25)

来源:互联网 发布:sql server 2005精简版 编辑:程序博客网 时间:2024/04/28 17:02

本篇介绍几个比较重要的概念,后续基于 OData Model 实现 CRUD。

  • REST

REST (Representational State Transfer) 这个词,是 Roy Thomas Fielding 在他 2000 年的博士论文中提出的,翻译成中文大意为表现层状态传输。由于他是 HTTP 协议(1.0 版和 1.1 版)的主要设计者、Apache 服务器软件的作者之一、Apache 基金会的第一任主席,所以 REST 原则迅速流行起来。当一个软件架构符合 REST 原则,我们称之为 RESTful 架构。

  • OData

开放数据协议(Open Data Protocol,缩写 OData)是一种描述如何创建和访问 Restful 服务的 OASIS 标准。该标准由微软发起,前三个版本1.0、2.0、3.0 都是微软开放标准。第四个版本4.0 于 2014 年 3 月 17 日在 OASIS 投票通过成为开放工业标准。

OData 是用来查询和更新数据的一种 Web协议,提供了把存在于应用程序中的数据暴露出来的方式。OData 运用且构建于很多 Web 技术之上,比如 HTTP、Atom Publishing Protocol(AtomPub)和 JSON,提供了从各种应用程序、服务和存储库中访问信息的能力。OData 被用来从各种数据源中暴露和访问信息, 这些数据源包括但不限于:关系数据库、文件系统、内容管理系统和传统 Web 站点。

前面说到 Rest 只是一种设计 Web 服务的思想,不是一种标准化的协议。正由于缺乏标准化,从而导致各家公布的 Restful API 统一通用方面的欠缺。OData 就是为弥补这种欠缺而被提出来的标准协议。

查看 Northwind OData Service

http://services.odata.org/ 这个网站提供了 OData data service 的示例,并且可以对 OData 数据进行 CRUD 操作。我们首先通过查看这些数据,了解 OData 的知识点。

我们先在浏览器中输入 http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/, 网站以 xml 格式提供了 Northwind 示例数据。这个是 Microsoft 经常使用的一个示例数据库,MS Office 套件中的 Access 数据库也可以看到。为了方便数据查看,建议使用 Chrome 的插件:Postman。Chrome 在查看 xml 和 json 数据格式上,比较难看。或者使用 IE 浏览器。以下是使 Postman 插件的查看效果:

以 json 格式显示数据

GET 请求默认以 xml 格式显示,在 URI 后加上 ?$format=json 则以 json 格式显示,如:http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/?$format=json 则以 json 格式显示。

元数据 ( metadata )

在 URI 后面加上 $metadata 则显示元数据:

Request:
- type: GET
- uri:http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/$metadata

Response

<?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">    <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">        <Schema Namespace="ODataDemo" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">            ...            <EntityType Name="Supplier">                <Key>                    <PropertyRef Name="ID" />                </Key>                <Property Name="ID" Type="Edm.Int32" Nullable="false" />                <Property Name="Name" Type="Edm.String" />                <Property Name="Address" Type="ODataDemo.Address" />                <Property Name="Location" Type="Edm.GeographyPoint" SRID="Variable" />                <Property Name="Concurrency" Type="Edm.Int32" ConcurrencyMode="Fixed" Nullable="false" />                <NavigationProperty Name="Products" Relationship="ODataDemo.Product_Supplier_Supplier_Products" ToRole="Product_Supplier" FromRole="Supplier_Products" />            </EntityType>            <ComplexType Name="Address">                <Property Name="Street" Type="Edm.String" />                <Property Name="City" Type="Edm.String" />                <Property Name="State" Type="Edm.String" />                <Property Name="ZipCode" Type="Edm.String" />                <Property Name="Country" Type="Edm.String" />            </ComplexType>                ...            <EntityContainer Name="DemoService" m:IsDefaultEntityContainer="true">                ...                <EntitySet Name="Suppliers" EntityType="ODataDemo.Supplier" />                ...                <FunctionImport Name="GetProductsByRating" ReturnType="Collection(ODataDemo.Product)" EntitySet="Products" m:HttpMethod="GET">                    <Parameter Name="rating" Type="Edm.Int16" Nullable="false" />                </FunctionImport>                <AssociationSet Name="Products_Advertisement_Advertisements" Association="ODataDemo.FeaturedProduct_Advertisement_Advertisement_FeaturedProduct">                    <End Role="FeaturedProduct_Advertisement" EntitySet="Products" />                    <End Role="Advertisement_FeaturedProduct" EntitySet="Advertisements" />                </AssociationSet>                <AssociationSet Name="Products_Categories_Categories" Association="ODataDemo.Product_Categories_Category_Products">                    ...                </AssociationSet>                 ...            </EntityContainer>            <Annotations Target="ODataDemo.DemoService">                <ValueAnnotation Term="Org.OData.Display.V1.Description" String="This is a sample OData service with vocabularies" />            </Annotations>            ...        </Schema>    </edmx:DataServices></edmx:Edmx>

为了更清楚看出文档结构,我省略了不相关的部分。
介绍一下元数据的重点。元数据用于定义 Odata 的数据结构。下面的图来自 [Manually creating a data model to use in SAP Web IDE’s Mock Data server] (
https://www.sap.com/developer/tutorials/hcp-webide-create-odata-model.html),能够很好地说明 metadata 的构成:

  • dataServiceVesion: data service 的版本
  • EntitiContainer 总体确定包括那些 EntitySet,比如 Northwind 包括 Products, Suppliers 等等。
<EntityContainer Name="DemoService" m:IsDefaultEntityContainer="true">    <EntitySet Name="Products" EntityType="ODataDemo.Product" />    <EntitySet Name="ProductDetails" EntityType="ODataDemo.ProductDetail" />    <EntitySet Name="Categories" EntityType="ODataDemo.Category" />    <EntitySet Name="Suppliers" EntityType="ODataDemo.Supplier" />    <EntitySet Name="Persons" EntityType="ODataDemo.Person" />    <EntitySet Name="PersonDetails" EntityType="ODataDemo.PersonDetail" />    <EntitySet Name="Advertisements" EntityType="ODataDemo.Advertisement" />    ...</EntityContainer>
  • EntitySet 下包含 EntityType,比如 Suppliers 这个 Set 下面包含 Supplier 这个 Entity。Entity 中包含 key 和 Properties:
<EntityType Name="Supplier">    <Key>        <PropertyRef Name="ID" />    </Key>    <Property Name="ID" Type="Edm.Int32" Nullable="false" />    <Property Name="Name" Type="Edm.String" />    <Property Name="Address" Type="ODataDemo.Address" />    <Property Name="Location" Type="Edm.GeographyPoint" SRID="Variable" />    <Property Name="Concurrency" Type="Edm.Int32" ConcurrencyMode="Fixed" Nullable="false" />    <NavigationProperty Name="Products" Relationship="ODataDemo.Product_Supplier_Supplier_Products" ToRole="Product_Supplier" FromRole="Supplier_Products" /></EntityType><ComplexType Name="Address">    <Property Name="Street" Type="Edm.String" />    <Property Name="City" Type="Edm.String" />    <Property Name="State" Type="Edm.String" />    <Property Name="ZipCode" Type="Edm.String" />    <Property Name="Country" Type="Edm.String" /></ComplexType>

查看 Entity Set

Request:

  • Type: GET
  • URL: http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/Suppliers/?$format=json

Response:

{  "odata.metadata": "http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/$metadata#Suppliers",  "value": [    {      "ID": 0,      "Name": "Exotic Liquids",      "Address": {        "Street": "NE 228th",        "City": "Sammamish",        "State": "WA",        "ZipCode": "98074",        "Country": "USA"      },      "Location": {        "type": "Point",        "coordinates": [          -122.03547668457,          47.6316604614258        ],        "crs": {          "type": "name",          "properties": {            "name": "EPSG:4326"          }        }      },      "Concurrency": 0    },    {      "ID": 1,      "Name": "Tokyo Traders",      "Address": {        "Street": "NE 40th",        "City": "Redmond",        "State": "WA",        "ZipCode": "98052",        "Country": "USA"      },      "Location": {        "type": "Point",        "coordinates": [          -122.107711791992,          47.6472206115723        ],        "crs": {          "type": "name",          "properties": {            "name": "EPSG:4326"          }        }      },      "Concurrency": 0    }  ]}

查看单条 Entity

比如,我们想查看第一个供应商:

Request:
- Type: GET
- URL: http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/Suppliers(0)/?$format=json

Response:

{  "odata.metadata": "http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/$metadata#Suppliers/@Element",  "ID": 0,  "Name": "Exotic Liquids",  "Address": {    "Street": "NE 228th",    "City": "Sammamish",    "State": "WA",    "ZipCode": "98074",    "Country": "USA"  },  ...}

查看 Entity 的相关 Property

比如,查看第一个供应商的名称:

Request:
- Type: GET
- URL: http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/Suppliers(0)/Name?$format=json

Response:

{  "odata.metadata": "http://services.odata.org/V3/(S(i2ebiza3pghgcecz3upusotg))/OData/OData.svc/$metadata#Edm.String",  "value": "Exotic Liquids"}

先了解这么多,还有 Navigation properties 等,以后再说。

OpenUI5 OData Model

SAP 提供了 sap.ui.model.odata.ODataModelsap.ui.model.odata.v2.ODataModelsap.ui.model.odata.v4.ODataModelsap.ui.model.odata.ODataModel 已经过时。Odata v2 model 目前支持到 OData 2.0。Odata v4 model 支持到 OData 4.0,但仅支持绑定模式。不支持代码模式,应该还在开发过程中。建议使用 Odata v2 model。

Odata v2 model 和 Odata model 的变化和区别请参见:OData v2 Model

OData Model 属于服务器端的数据模型,也就是说,客户端必须请求后,根据服务器的应答,才能看到请求的数据。

Same-origin 政策

OData 是一种基于 Web 的协议,数据访问受到 Same-origin policy 的限制。什么是 Same-origin policy? 根据 WIKI 的解释:

In computing, the same-origin policy is an important concept in the web application security model. Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. An origin is defined as a combination of URI scheme, hostname, and port number. This policy prevents a malicious script on one page from obtaining access to sensitive data on another web page through that page’s Document Object Model.

并且给出了一些示例方便我们理解:

如果我们直接对 services.odata.com 的进行 CRUD,因为违背了Same-origin 策略,会产生错误。解决办法:

  • 使用代理,比如https://cors-anywhere.herokuapp.com/
  • 在 SAP Web IDE 中通过 HCP (Hana Cloud Platform) 账号,由 SAP Web IDE 代理。

参考帖子:stackoverflow: access cross origin resources

通过 OData model 读取 OData 数据

v2 模型提供了两种方法,一是通过代码,二是通过数据绑定。我们先来看通过代码如何访问 OData 数据:

var sServiceUrl = "http://services.odata.org/V3/Northwind/Northwind.svc/";var oModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl);         sap.ui.getCore().setModel(oModel);oModel.read("/Products(1)", {    success: function(oData, oResponse){        console.log(oData);        console.log(oResponse);    },    error: function(oError){        console.log(oError);    }})

运行程序,Chrome 浏览器返回如下错误( F12 查看)

Failed to load resource: the server responded with a status of 501 (Not Implemented)index.html:1 XMLHttpRequest cannot load http://services.odata.org/V3/Northwind/Northwind.svc/$metadata. Response for preflight has invalid HTTP status code 501

var sServiceUrl = "http://services.odata.org/V3/Northwind/Northwind.svc/"; 语句改为:

var sServiceUrl = "https://cors-anywhere.herokuapp.com/http://services.odata.org/V3/Northwind/Northwind.svc/";

可以正常返回 oDataoResponse

第二种方法是通过控件绑定 OData 数据:

var sServiceUrl = "https://cors-anywhere.herokuapp.com/http://services.odata.org/V3/Northwind/Northwind.svc/";var oModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl);    sap.ui.getCore().setModel(oModel);var oText = new sap.m.Text({    text: "Product name: {ProductName}"});var oPage = new sap.m.Page("masterPage", {    title: "Product 1 information",    content: [oText]});oPage.bindElement("/Products(1)");var oApp = new sap.m.App();oApp.addPage(oPage);oApp.placeAt("content");

参考

RESTful API 最佳实践

OData 的初步认识

OData v2 model

[Manually creating a data model to use in SAP Web IDE’s Mock Data server](
https://www.sap.com/developer/tutorials/hcp-webide-create-odata-model.html)

Same-origin policy

0 0