65.9K
CodeProject 正在变化。 阅读更多。
Home

在 MEAN 堆栈上为 MongoDB 创建 OData 端点

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (9投票s)

2016年8月8日

MIT

8分钟阅读

viewsIcon

36032

downloadIcon

21

本文是一个关于在 MEAN 堆栈上为 MongoDB 数据库创建基本 OData(开放数据协议)端点的教程。

引言

对于初学者,这里有 OData 和 MEAN 堆栈的简要描述,以及我对其有用性的看法。

OData(开放数据协议)是一个 OASIS 标准,它定义了一套构建和消费 RESTful API 的最佳实践。OData 帮助您专注于业务逻辑,同时构建 RESTful API,而无需担心定义请求和响应头、状态码、HTTP 方法、URL 约定、媒体类型、有效负载格式、查询选项等各种方法。OData 还提供了跟踪更改、为可重用过程定义函数/操作以及发送异步/批量请求的指导。

-- http://www.odata.org/

由于 OData 是基于 HTTP 等核心协议并使用 REST 方法构建的,因此很容易构建可用于查询底层数据库的 RESTful API。客户端也很容易消费 OData Restful API,因为有标准的协议和 URL 约定用于查询、过滤、聚合、CRUD 操作和数据共享。OData API 的另一个优点是元数据。消费 API 的开发人员可以轻松理解 API 数据模型的描述,并且 OData 元数据是机器可读的。

MEAN 是一个免费的开源 JavaScript 软件栈,用于构建动态网站和 Web 应用程序。MEAN 堆栈使用 MongoDBExpress.jsAngular.jsNode.js。由于 MEAN 堆栈的所有组件都支持用 JavaScript 编写的程序,因此 MEAN 应用程序可以在一个语言中编写,用于服务器端和客户端执行环境。

-- https://en.wikipedia.org/wiki/MEAN_(software_bundle)

MEAN 堆栈的一大优势是其所有组件都是开源的,并且定期更新,免费使用。另一个优势是客户端和服务器端都使用 JavaScript,这使得 Web 开发人员很容易成为全栈开发人员。

背景

我一直在寻找一种标准方法来在 MEAN 堆栈上的 MongoDB 数据库上进行查询、过滤、搜索和执行 CRUD 操作。虽然有很多关于使用 Microsoft .NET 堆栈构建 OData 端点的教程,但我找不到任何从头到尾关于在 MEAN 堆栈上构建简单 OData 端点的教程。我的想法是分享我通过 Web 和开源技术学到的知识,希望它能帮助其他人。

必备组件

如果您打算按照本教程操作,您需要安装以下项目

1) 从 https://node.org.cn 安装适用于您机器的 node.js。我下载并安装了适用于 Windows (x64) 的 node-v4.4.7-x64.msi。

2) 从 https://mongodb.ac.cn 安装适用于您机器的 MongoDB。我下载了适用于 Windows Server 2008 R2 64 位及更高版本(带 SSL 支持)的 MongoDB Community Server 3.2.7 版本,并将其安装在我的 Windows 7 64 位机器上。有关安装的更多信息,请参阅 https://docs.mongodb.com/getting-started/shell/installation/

让我们开始吧

在本节中,我们将为我们的 OData 端点创建一个工作应用程序目录“odataapp”和 package.json 文件。我们还将在应用程序目录中安装 Express 和其他 npm 包。

创建应用程序目录

创建一个目录来存放您的应用程序,并将其设置为您的工作目录。在我的机器上,工作目录位于“C:\projects\odataapp”。打开命令提示符,并将目录更改为您的工作目录。使用 npm init 命令为您的应用程序创建一个 package.json 文件。

npm init

如下图所示,此命令会提示您输入一些信息,例如应用程序的名称和版本。目前,您可以直接按 RETURN 键接受大多数默认值。

create package.json file in the working folder

在应用程序目录中安装 Express,并通过运行以下命令将其保存到 package.json 文件的依赖项列表中。

npm install express --save

既然我们讨论的是 MEAN 堆栈,我们将把数据存储在 MongoDB 中。安装适用于 mongodb 的 npm 包。

npm install mongodb --save

安装其他 npm 包。在本教程中,我使用 simple-odata-server 包为 MongoDB 集合创建 OData 端点。

npm install simple-odata-server --save

安装以下软件包是可选的。安装跨域资源共享(CORS)软件包将使应用程序能够服务来自端点域之外的其他域的 AJAX (XMLHttpRequest) 请求。

npm install cors --save

下图显示了我在应用程序目录中安装 simple-odata-server 和 cors 包后的命令提示符。

Installing npm packages

成功安装软件包后,“package.json”文件应包含所有列出的依赖项,如下所示。

{
  "name": "odataapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.7.1",
    "express": "^4.14.0",
    "mongodb": "^2.2.1",
    "simple-odata-server": "^0.2.0"
  }
}

导入数据到 MongoDB 进行演示

在本教程中,我们将使用 MongoDB 附带的 'mongoimport' 工具将 files\products.json 中的数据导入到 MongoDB 服务器。您可以使用以下命令或 RoboMongo 或您选择的任何其他工具导入数据。我已经将数据文件包含在源代码中,并突出显示了下图中的导入命令。数据被导入到 MongoDB 服务器的 'demo' 数据库的 'products' 集合中。

如果数据成功导入到 demo 数据库的 'products' 集合中,您可以使用以下命令在 Mongo shell 中查看数据。

use demo
db.products.find()

如果您正在使用 RoboMongo,查询“products”集合时应该会看到 11 个项目。

使用代码

在 package.json 中,注意主字段中列出的文件。在我们的演示中,我们将其保留为 index.js。它是应用程序的主要入口点。让我们在应用程序目录中创建一个新的 index.js 文件,其中包含以下代码。您可以使用 Visual Studio Code、Sublime Text 或您选择的任何代码编辑器来创建和编辑此文件。我在变量和方法之前添加了注释,以帮助您理解代码。

// Assign the required packages and dependencies to variables
var express = require('express');
var ODataServer = require("simple-odata-server");
var MongoClient = require('mongodb').MongoClient;
var cors = require("cors");

// Create app variable to initialize Express 
var app = express();

// Enable Cross-origin resource sharing (CORS)  for app.
app.use(cors());

// Define Odata model of the resource entity i.e. Product. 
// The metadata is defined using OData type system called the Entity Data Model (EDM),
// consisting of EntitySets, Entities, ComplexTypes and Scalar Types.
var model = {
    namespace: "demo",
    entityTypes: {
        "Product": {
            "_id": {"type": "Edm.String", key: true},        
            "ProductNum": {"type": "Edm.Int32"},
            "Name": {"type": "Edm.String"},  
            "Description": {"type": "Edm.String"}, 
            "ReleaseDate": {"type": "Edm.DateTime"},  
            "DiscontinuedDate": {"type": "Edm.DateTime"},  
            "Rating": {"type": "Edm.Int32"},
            "Price": {"type": "Edm.Double"}                    
        }
    },   
    entitySets: {
        "products": {
            entityType: "demo.Product"
        }
    }
};

// Instantiates ODataServer and assigns to odataserver variable.
var odataServer = ODataServer()
                  .model(model);

// Connection to demo database in MongoDB
MongoClient.connect("mongodb:///demo", function(err, db) {
    odataServer.onMongo(function(cb) { cb(err, db); });
});

// The directive to set app route path.
app.use("/odata", function (req, res) {
        odataServer.handle(req, res);
    });

// The app listens on port 3010 and prints the endpoint URI in console window.
var server = app.listen(3010, function () {
    console.log('Server running at http://127.0.0.1:3010/');
});

让我们通过在工作目录打开命令提示符并运行 node index.js 来运行应用程序,如下所示。如果一切正常,您应该会看到服务器运行的控制台消息。

查询集合

让我们向在本地 Node 服务器端口 3010 运行的 OData 端点 index.js 发送一些 OData 查询。在下面的示例中,我使用 URL https://:3010/odata/$metadata 请求元数据。

让我们通过 HTTP GET 请求从 OData 端点请求数据。下面的请求返回 MongoDB 服务器中“demo”数据库中的“products”集合。

我正在 Google Chrome 浏览器中发出 HTTP 请求。我已在 Chrome 中添加了 JSONView 应用程序以查看 JSON 文档。您可以使用 Fiddler 或 Postman 或您选择的任何其他开发工具来发送 HTTP 请求和响应。

下面是按 ID 请求单个实体的示例。

让我们发送一个 get 请求 https://:3010/odata/products?$filter=Name eq 'Bread' 来过滤集合中 Name 等于 'Bread' 的数据。

您可以尝试以下 OData 查询来筛选、排序、统计 MongoDB 中的产品集合。

$filter 查询选项允许客户端筛选资源集合
https://:3010/odata/products?$filter=Name eq 'Bread' or Name eq 'Milk'

$top 查询选项请求查询集合中包含在结果中的项目数。
https://:3010/odata/products?$top=5

$skip 查询选项请求查询集合中要跳过且不包含在结果中的项目数。
https://:3010/odata/products?$skip=10

$select 查询选项允许客户端为每个实体请求一组有限的属性
https://:3010/odata/products?$select=Name
https://:3010/odata/products?$select=Name, Description

$orderby 查询选项允许客户端使用 asc 升序或 desc 降序请求资源。如果未指定 asc 或 desc,则资源将按升序排列。
https://:3010/odata/products?$orderby=ProductNum desc
https://:3010/odata/products?$orderby=Rating

$count 选项允许客户端请求集合中的计数。
https://:3010/odata/products/$count

根据上述查询,您可以看到 OData 端点可以是通用的 WebAPI,以标准方式查询集合。例如,我可以使用以下 OData 查询获取集合中最昂贵的三种产品及其 Id、名称、描述和价格。
https://:3010/odata/products?$select=Name, Description, Price&$orderby=Price desc&$top=3

我们还可以使用 OData 端点对我们的产品集合执行 CRUD 操作。为了演示这一点,我将使用 Chrome 浏览器中的 Postman 应用程序。

创建一个实体

我们可以向我们的 OData 端点发送 POST 请求,以在我们的数据库中添加一个新产品。以下是我们将 POST 到我们的端点 https://:3010/odata/products 的新产品。

{
"ProductNum": 12,
"Name": "Chai",
"Description": "Bulk packet of Darjeeling Tea",
"ReleaseDate": "1992-12-31T00:00:00Z",
"DiscontinuedDate": null,
"Rating": 1,
"Price": 5.99
}

在下图中,向 OData 端点发送上述 POST 请求后,您可以看到“Status: 201 Created”。请求已成功处理,并创建了一个新资源。在我演示数据库中创建的新产品的 Id 是“5799f7048e68450964afc81f”。我们将在下一节中使用此 Id 来更新新产品。

更新实体

OData 遵循 RESTful API 原则,允许 HTTP 方法 PATCH 和 PUT 执行更新。PATCH 执行部分更新,并且比 PUT 更受推荐。客户端只指定要更新的属性。PUT 替换整个实体,客户端必须为实体中的所有属性发送值,包括未更改的值。

我们将向 OData 端点 https://:3010/odata/products('5799f7048e68450964afc81f') 发送 PATCH 请求,以更改产品的名称和价格。

可以通过访问资源 URI https://:3010/odata/products('5799f7048e68450964afc81f') 在 MongoDB 数据库中查看更改。

请注意,更新和删除请求的 URI 中应传递产品 ID。

删除实体

我们还可以使用 OData 端点通过在 DELETE 请求中传递 ID 来删除数据库中的产品。

如果 DELETE 操作成功,查询 https://:3010/odata/products/$count 应该会减少一个值。

结论、限制和增强思路

在本教程中,我构建了一个基本的 OData 端点,它可以作为 WebAPI 来提供基本的数据操作,例如选择、过滤、排序、top、skip、计数、创建、更新和删除。我使用各种 npm 模块在 MEAN 堆栈上构建了 OData 端点。该端点受到模块支持的功能的限制。好的一点是,对于任何增强功能,您可以将模块替换为自己的或其他符合您要求的第三方模块。https://npmjs.net.cn 上有许多开源模块。对于面向公众的 WebAPI,您可能希望通过启用身份验证和授权来增强 API 的安全性。WebAPI 的安全性本身就是一个话题。如果符合您的要求,您可以使用身份验证包 Passport。由于 OData 端点是基于 REST 的,因此您可以使用 OAuth2、OpenID、bearer token 或任何其他标准来保护 Web 服务。

© . All rights reserved.