Feed Collection的建模怎么实现

47次阅读
没有评论

共计 7405 个字符,预计需要花费 19 分钟才能阅读完成。

本篇内容主要讲解“Feed Collection 的建模怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“Feed Collection 的建模怎么实现”吧!

MEAN stack 可概括为:

M = MongoDB/Mongoose.js。流行的数据库,对 node . js 来说是一个优雅的 ODM。

E = Express.js : 一个轻量级 Web 应用程序框架。

A = Angular.js : 一个健壮的框架用于创建 HTML5 和 JavaScript-rich Web 应用程序。

N = Node.js 服务器端 JavaScript interpreter。

MEAN stack 是 LAMP (Linux、Apache、MySQL,PHP / Python) stack 的一个现代替代者,在九十年代末,LAMP 曾是 Web 应用程序的主流构建方式。

在这个应用程序中并不会使用 Angular.js,因为这里并不是要构建一个 HTML 用户界面。相反,这里创建的是一个没有用户界面的 REST API,但它却可以作为任何界面的基础,如一个网站、一个 Android 应用程序,或者一个 iOS 应用程序。也可以说我们正在 ME(a)N stack 上构建 REST API,但这不是重点! ##REST API 是什么?

REST 代表 Representational State Transfer,是 SOAP 和 WSDL XML-based API 协议的一个更轻量级替代方案。

REST 使用客户端 - 服务器模型,服务器是一个 HTTP 服务器,而客户端发送 HTTP 行为 (GET、POST、PUT、DELETE),以及 URL 编码的变量参数和一个 URL。URL 指定了对象的作用范围,而服务器则会通过结果代码和有效的 JavaScript Object Notation (JSON) 进行响应。

因为服务器用 JSON 回复,MongoDB 与 JSON 又可以很好地交互,同时所有组件都使用了 JavaScript,因此 MEAN stack 非常适合本用例中的应用程序。在进入开始定义数据模型后, 你会看到一些 JSON 的例子。

CRUD 缩略词常被用来描述数据库操作。CRUD 代表创建、读取、更新和删除。这些数据库操作能很好地映射到 HTTP 动作:

POST:客户想要插入或创建一个对象。

GET:客户端想要读取一个对象。

PUT:客户想要更新一个对象。

DELETE:客户想要删除一个对象。

在定义 API 后,这些操作将变得更加直观。REST APIs 中通常会使用的一些常见 HTTP 结果代码如下:

200 ——“OK”。

201 ——“Created”(和 POST 一起使用)。

400 ——“Bad Request”(可能丢失所需参数)。

401 ——“Unauthorized”(身份验证参数丢失)。

403 ——“Forbidden”(已验证,但是权限不够)。

404 ——“Not Found”。

RFC 文档中可以找到一个完整的描述,这个在本博客末尾的参考资料中列出。上面这些结果代码都会在本应用程序中使用,随后就会展示一些例子。为什么从 REST API 开始?

部署一个 REST API 可以为建立任何类型应用程序打下基础。如前文所述,这些应用程序可能会基于网络或者专门针对某些平台设计,比如 Android 或者 iOS。

时下,已经有许多公司在建立应用程序时不再使用 HTTP 或者 Web 接口,比如 Uber、WhatsApp、Postmates 和 Wash.io。从一个简单的应用程序发展成一个强大的平台,REST API 可以大幅度简化这个过程中其他接口和应用程序的实现。## 建立 REST API

这里会建立一个 RSS Aggregator,类似 Google Reader,应用程序主要会包含两个组件:

REST API

Feed Grabber(类似 Google Reader)

本系列博文都将聚焦这个 REST API 的打造,不会去关注 RSS feeds 的复杂性。现在,Feed Grabber 的代码已经可以在 github repository 中发现,详情可以见博文列出的资源。下面将介绍打造这个 API 所需的步骤。首先会根据具体需求来定义数据模型:

在用户账户中储存用户信息

跟踪需要被监视的 RSS feeds

将 feed 记录 pull 到数据库

跟踪用户 feed 订阅

跟踪用户会阅读哪个订阅的 feed

用户则需要可以完成下列操作:

建立一个账户

到 feed 的订阅或者退订

阅读 feed 记录

标记 feed/ 记录的阅读状态(已读 / 未读)

## 数据建模

这里不会深入讨论 MongoDB 中的数据建模,详细资料可以在博文后的列举的资料中发现。本用例需要 4 个 collections 来管理这个信息:

Feed collection

Feed entry collection

User collection

User-feed-entry mapping collection

##Feed Collection

下面一起进入一段代码,Feed Collection 的建模可以通过下述 JSON 文档完成:

{_id : ObjectId( 523b1153a2aa6a3233a913f8),
 requiresAuthentication : false,
 modifiedDate : ISODate(2014-08-29T17:40:22Z),
 permanentlyRemoved : false,
 feedURL :  http://feeds.feedburner.com/eater/nyc ,
 title :  Eater NY ,
 bozoBitSet : false,
 enabled : true,
 etag :  4bL78iLSZud2iXd/vd10mYC32BE ,
 link :  http://ny.eater.com/ ,
 permanentRedirectURL : null,
 description :  The New York City Restaurant, Bar, and Nightlife Blog”}

如果你精通关系型数据库技术,那么你将了解数据库、表格、列和行。在 MongoDB 中,大部分的关系型概念都可以映射。从高等级看,MongoDB 部署支持 1 个或者多个数据库。1 个数据库可能包含多个 collection,这个类似于传统关系型数据库中的表格。Collection 中会有多个 document,从高等级看,document 相当于关系型数据库中的行。这里需要注意的是,MongoDB 中的 document 并没有预设的格式,取而代之,每个 document 中都可以有 1 个或者多个的键值对,这里的值可能是简单的,比如日期,也可以是复杂的,比如 1 个地址对象数组。

上文的 JSON 文档是一个 Eater Blog 的 RSS feed 示例,它会跟踪纽约所有餐馆信息。因此,这里可能存在许多字段,而用例中主要关注的则是 feed 中的 URL 以及 description。描述是非常重要的,因此在建立一个移动应用程序时,它会是 feed 一个很好的摘要。

JSON 中的其他字段用于内部使用,其中非常重要的字段是 _id。在 MongoDB 中,每个 document 都需要拥有一个 _id 字段。如果你建立一个没有 —— id 的 document,MongoDB 将为你自动添加。在 MongoDB 中,这个字段就是主键的存在,因此 MongoDB 会保证这个字段值在 collection 范围唯一。##Feed Entry Collection

在 feed 之后,用例中还期望追踪 feed 记录。下面是一个 Feed Entry Collection 文档示例:

{  _id : ObjectId( 523b1153a2aa6a3233a91412),
  description :  Buzzfeed asked a bunch of people...”,
  title :  Cronut Mania: Buzzfeed asked a bunch of people... ,
  summary :  Buzzfeed asked a bunch of people that were...”,
  content : [{
  base :  http://ny.eater.com/ ,
  type :  text/html ,
  value : ”LOTS OF HTML HERE  ,
  language :  en 
 }],
  entryID :  tag:ny.eater.com,2013://4.560508 ,
  publishedDate : ISODate(2013-09-17T20:45:20Z),
  link :  http://ny.eater.com/archives/2013/09/cronut_mania_41 .php ,
  feedID : ObjectId(523b1153a2aa6a3233a913f8)
}

再次提醒,这里同样必须拥有一个 _id 字段,同时也可以看到 description、title 和 summary 字段。对于 content 字段,这里使用的是数组,数据中同样储存了一个 document。MongoDB 允许通过这种方式嵌套使用 document,同时这个用法在许多场景中也是非常必要的,因为用例往往需求将信息集中存储。

entryID 字段使用了 tag 格式来避免复制 feed 记录。这里需要注意的是 feedID 和 ObjectId 的用法——值则是 Eater Blog document 的 _id。这提供了一个参考模型,类似关系型数据库中的外键。因此,如果期望查看这个 ObjectId 关联的 feed document,可以取值 523b1153a2aa6a3233a913f8,并在 _id 上查询 feed collection,从而就会返回 Eater Blog document。##User Collection

这里有一个用户需要使用的 document:

{  _id  : ObjectId( 54ad6c3ae764de42070b27b1),
  active  : true,
  email  :  testuser1@example.com ,
  firstName  :  Test ,
  lastName  :  User1 ,
  sp_api_key_id  :  6YQB0A8VXM0X8RVDPPLRHBI7J ,
  sp_api_key_secret  :  veBw/YFx56Dl0bbiVEpvbjF”,
  lastLogin  : ISODate(2015-01-07T17:26:18.996Z),
  created  : ISODate(2015-01-07T17:26:18.995Z),
  subs  : [ ObjectId( 523b1153a2aa6a3233a913f8),
 ObjectId(54b563c3a50a190b50f4d63b) ],
}

用户应该有 email 地址、first name 和 last name。同样,这里还存在 sp_api_key_id 和 sp_api_key_secret —— 在后续部分会结合 Stormpath(一个用户管理 API)使用这两个字段。最后一个字段 subs,是 1 个订阅数组。subs 字段会标明这个用户订阅了哪些 feeds。##User-Feed-Entry Mapping Collection

{  _id  : ObjectId( 523b2fcc054b1b8c579bdb82),
  read  : true,
  user_id  : ObjectId(54ad6c3ae764de42070b27b1),
  feed_entry_id  : ObjectId(523b1153a2aa6a3233a91412),
  feed_id  : ObjectId(523b1153a2aa6a3233a913f8)
}

最后一个 collection 允许映射用户到 feeds,并跟踪哪些 feeds 已经读取。在这里,使用一个布尔类型(true/false)来标记已读和未读。##REST API 的一些功能需求

如上文所述,用户需要可以完成以下操作:

建立一个账户

到 feed 的订阅或者退订

阅读 feed 记录

标记 feed / 记录的阅读状态(已读 / 未读)

此外,用户还需求可以重置密码。下表表示了这些操作是如何映射到 HTTP 路由和动作。

在生产环境中,HTTP(HTTPS)安全需求使用一个标准的途径来发送敏感信息,比如密码。## 通过 Stormpath 实现现实世界中的身份验证

在一个鲁棒的现实世界应用程序中,提供用户身份验证不可避免。因此,这里需要一个安全的途径来管理用户、密码和密码重置。

在本用例中,可以使用多种方式进行身份验证。其中一个就是使用 Node.js 搭配 Passport Plugin,这个方式通常被用于社交媒体账户验证中,比如 Facebook 或者 Twitter。然而,Stormpath 同样是一个非常不错的途径。Stormpath 是一个用户管理即服务,支持身份验证和通过 API keys 授权。根本上,Stormpath 维护了一个用户详情和密码数据库,从而客户端应用程序 API 可以调用 Stormpath REST API 来进行用户身份验证。

下图显示了使用 Stormpath 后的请求和响应流。

详细来说,Stormpath 会为每个应用程序提供一个安全秘钥,通过它们的服务来定义。举个例子,这里可以定义一个应用程序作为「Reader Production」或者「Reader Test」。如果一直对应用程序进行开发和测试,定义这两个应用程序非常实用,因为增加和删除测试用户会非常频繁。在这里,Stormpath 同样会提供一个 API Key Properties 文件。Stormpath 同样允许基于应用程序的需求来定义密码属性,比如:

不低于 8 个字符

必须包含大小写

必须包含数字

必须包含 1 个非字母字符

Stormpath 会跟踪所有用户,并分配他们的 API keys(用于 REST API 身份验证),这将大幅度简化应用程序建立过程,因为这里不再需要为验证用户编写代码。##Node.js

Node.js 是服务器端和网络应用程序的运行时环境。Node.js 使用 JavaScript 并适合多种不同的平台,比如 Linux、Microsoft Windows 和 Apple OS X。

Node.js 应用程序需要通过多个库模块建立,当下社区中已经有了非常多的资源,后续应用程序建立中也会使用到。

为了使用 Node.js,开发者需要定义 package.json 文件来描述应用程序以及所有库的依赖性。

Node.js Package Manager 会安装所有库的副本到应用程序目录的一个子目录,也就是 node_modules/。这么做有一定的好处,因为这样做可以隔离不同应用程序的库版本,同时也避免了所有库都被统一安装到标准目录下造成的代码复杂性,比如 /usr/lib。

命令 npm 会建立 node_modules/ 目录,以及所有需要的库。

下面是 package.json 文件下的 JavaScript:

{
  name :  reader-api ,
  main :  server.js ,
  dependencies : {
 express  :  ~4.10.0 ,
  stormpath  :  ~0.7.5 ,  express-stormpath  :  ~0.5.9 ,
  mongodb  :  ~1.4.26”,  mongoose  :  ~3.8.0 ,
  body-parser  :  ~1.10.0”,  method-override  :  ~2.3.0 ,
  morgan  :  ~1.5.0”,  winston  :  ~0.8.3”,  express-winston  :  ~0.2.9 ,
 validator  :  ~3.27.0 ,
  path  :  ~0.4.9 ,
  errorhandler  :  ~1.3.0 ,
  frisby  :  ~0.8.3 ,
  jasmine-node  :  ~1.14.5 ,
  async  :  ~0.9.0 
 }
}

应用程序被命名为 reader-api,主文件被命名为 server.js,随后会是一系列的依赖库和它们的版本。这些库其中的一些被设计用来解析 HTTP 查询。在这里,我们会使用 frisby 作为测试工具,而 jasmine-node 则被用来运行 frisby 脚本。

在这些库中,async 尤为重要。如果你从未使用过 node.js,那么请注意 node.js 使用的是异步机制。因此,任何阻塞 input/output (I/O) 的操作(比如从 socket 中读取或者 1 个数据库查询)都会采用一个回调函数作为最后的参数,然后继续控制流,只有在阻塞操作结束后才会继续这个回调函数。下面看一个简单的例子来理解这一点。

function foo() { someAsyncFunction(params, function(err, results) { console.log(“one”);
 }); console.log(“two”); }

在上面这个例子中,你想象中的输出可能是:

one
two

但实际情况的输出是:

two
one

造成这个结果的原因就是 Node.js 使用的异步机制,打印「one」的代码可能会在后续的回调函数中执行。之所以说可能,是因为这只在一定的情景下发生。这种异步编程带来的不确定性被称之为 non-deterministic execution。对于许多编程任务来说,这么做可以获得很高的性能,但是在顺序性要求的场景则非常麻烦。而通过下面的用法则可以获得一个理想中的顺序:

actionArray = [ function one(cb) { someAsyncFunction(params, function(err,
 results) { if (err) { cb(new Error(“There was an error”)); } console.log(“one”);
 cb(null); }); }, function two(cb) { console.log(“two”); cb(null); } ] async.series(actionArray);

到此,相信大家对“Feed Collection 的建模怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计7405字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)