美文网首页
SAPUI5 (25) - 了解 OData 和 OpenUI5

SAPUI5 (25) - 了解 OData 和 OpenUI5

作者: Stone0823 | 来源:发表于2017-03-10 16:37 被阅读872次

    本篇介绍几个比较重要的概念,后续基于 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 插件的查看效果:

    image

    以 json 格式显示数据

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

    元数据 ( metadata )

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

    Request:

    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 的构成:

    image
    • 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:

    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:

    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:

    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

    image

    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.

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

    image

    如果我们直接对 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
    Same-origin policy

    相关文章

      网友评论

          本文标题:SAPUI5 (25) - 了解 OData 和 OpenUI5

          本文链接:https://www.haomeiwen.com/subject/oucogttx.html