
一个使用 MongoDB Atlas、Microsoft Azure 和无服务器功能的定制 WordPress 仪表盘!





0/5 (0投票)
我正在为一位企业客户构建一个定制的 WordPress 仪表盘,它由 React.js 驱动,基于 Node.js,并使用 MongoDB Atlas 作为数据库。
TL;DR 我正在为一位企业客户构建一个定制的 WordPress 仪表盘,它由 React.js 驱动,基于 Node.js,并使用 MongoDB Atlas 作为数据库。
这个仪表盘使用多个 Microsoft Azure 服务,例如认知服务、Azure 应用服务,特别是无服务器 Azure 函数。在这篇文章中,您将学习如何构建其中的一个小模块,以及我选择技术栈、应用程序和产品的原因。
我的一位企业客户拥有一家大型网络和媒体公司,他搭建了一个大规模的 WordPress 网站。他最近咨询我是否有可能构建一个定制的 WordPress 仪表盘(基于 WordPress REST API),以帮助他通过机器学习和人工智能做出明智的商业决策。
随着 JavaScript 席卷全球,以及 WordPress 通过创建 Gutenberg 项目来适应这一趋势,我想到了一个架构/技术栈:WordPress 作为内容层,一个熟悉且经过实战考验的环境,它能很好地完成任务,同时有一个用 JavaScript 构建的定制仪表盘。
当您被要求构建一个现代 JavaScript 应用程序时,您会发现自己身处各种框架、工具和开发工作流之中。在过去几年里,JavaScript 生态系统发展壮大了很多。我们现在有许多非常好的选择。
因此,在研究了一段时间我的选择之后,我决定使用 React.js 和 Node.js 来开始构建定制的 WordPress 仪表盘。尽管该项目目前处于构思阶段,但我认为在这里分享我们的一些目标很重要,以定义我选择技术栈的背景。
定制 WordPress 仪表盘的目标
想象一下您拥有一家大型网络公司,其中有超过 500 家酒店(分布在三个不同的国家)使用您的服务来支持他们的会议厅、IT 会议和在线物业管理,例如网站和博客。这就是我的客户所做的事情。
其中大部分由一个庞大的多站点 WordPress 实例提供支持,该实例管理酒店、网站、在线预订、注册、活动、门票、评论和留言的一切事务。还有其他系统运行不同的软件,能够通过 REST API 生成内容。
我们着手创建了一个定制的 WordPress 仪表盘,考虑到许多目标,但我列出了其中一些与本文相关的目标。看看我目前为止构建了什么,它们都基于无服务器 Azure 函数——这些功能非常棒。
高级数据报告
定制仪表盘将报告所有高级数据,例如我的客户投资组合(500+ 家酒店)中的实时销售情况,基于实体/时间和基于日期的细分。
以及他的每个特许经营店每天、每周、每月表现如何。所有这些数据都输入到 MongoDB Atlas 中。稍后会详细介绍。
无服务器自动化
大多数模块都将基于无服务器架构构建——这在这种情况下提供了巨大的好处。所有自动化都始终运行,并且按使用付费,即用多少付多少。
初步粗略估算,此解决方案比始终运行的服务器虚拟机经济 34%。我们正在使用 Azure Functions 进行这种无服务器自动化。
物联网 (IoT) 中心
我的客户大约有 200 名 IT 经理,他们拥有支持物联网的设备,这些设备将数据输入到多个在线项目中。这个定制的仪表盘还包括这些数据,以便做出更好的决策,并将整个注册、管理、维护团队的中心连接到一个地方。
您可能已经猜到,这个项目利用了 Microsoft Azure 的 IoT Hub 来连接、监控和管理所有物联网资产。
机器学习与人工智能
我们正在使用 Microsoft Azure 的许多不同服务,通过机器学习使这个仪表盘具有人工智能。
有一个巨大的数据集被输入到 ML Studio,这有助于我们预测不同的决策,例如空间管理、IT 活动的低注册趋势,以及这些事情发生的原因和时间等问题。
虽然机器学习部分超出了本文的范围,但我仍然计划通过 Azure 的 认知服务 来介绍一些我已实现的出色人工智能。
实时 & 即时
这个定制仪表盘最重要的一个方面是它是实时和即时的。这意味着我需要一个托管数据库,能够处理如此大量的数据,同时保持高可用性。
但与此同时,它用于管理目的,不需要对 WordPress 网站产生任何影响。这是这个仪表盘的关键系统设计决策。
我的意思是,我们可以对这个定制仪表盘进行各种实验,但它不应该对运行多站点 WordPress 实例的数据库/服务器产生任何影响。
MongoDB & MongoDB Atlas
对于这个定制的 WordPress 仪表盘,我正在使用 MongoDB Atlas 作为 DBaaS(数据库即服务)。我非常满意。当我第一次分享我将使用 MongoDB 时,许多开发人员表示担忧。
大多数问题都是关于为什么要通过添加另一个数据库来增加另一层复杂性。为什么不直接使用 WordPress 数据库。为了回答这些问题以及更多问题,我准备了一系列关于我为什么要使用 MongoDB Atlas 的原因。
不喜欢关系型数据库
我个人不喜欢关系型数据库。大多数时候,对我来说,它们妨碍了应用程序的构建。我必须完全跳出我正在构建的应用程序,考虑我未来的数据库并设计一个好的模式,这总是导致我的开发工作流程变得糟糕。充其量是反直觉的——至少对我来说是这样。
硬盘便宜 | CPU/内存不便宜
旧数据库的设计主要是为了节省磁盘空间等。这导致了大量问题,如规范化、索引,并使分片、自动伸缩和复制变得更加困难。
如今,磁盘空间非常便宜。另一方面,CPU/内存却不便宜,如果您在这里做出错误的选择,您的系统管理员成本可能会迅速飙升。
就像您想创建一个定制仪表盘,但您的系统设计架构师因为他选择的设计方式让您花费了两个系统管理员的成本。同样,我的客户想要一个托管解决方案,而无需雇佣一个 IT/DevOps 团队——至少对于一个实验性的定制仪表盘来说是这样。
MongoDB 的优点
- 无模式。灵活的模式是胜利的保证。我无需更改任何内容,我的常规应用程序开发工作流,创建一个基于 Node.js 的应用程序,其中我正在操作 JSON 类型数据,我可以直接将其输入到 MongoDB 中,它就能正常工作。
- 工作流一致性。它创建的文档与我的自定义仪表盘的表示方式一致。销售、视频、讲座、评论、评价、注册等,所有这些在前端、后端甚至数据库中都具有相似的数据表示。我通过中间件管理第三方数据。这种一致性转化为简洁的代码。
- 易于横向扩展。它通过使用副本集来扩展读取。通过使用分片(自动平衡)来扩展写入。只需启动另一台机器即可。最重要的是,MongoDB 不像关系型数据库那样进行垂直扩展,而是允许您通过不同级别的一致性进行水平扩展。这是一个巨大的优势。
- 成本。当然,这取决于具体的 RDBMS,但 MongoDB 是免费的,可以在 Linux 上运行,非常适合在更便宜的通用设备上运行。
为什么选择 MongoDB Atlas?
嗯,既然我知道 MongoDB 是正确的数据库选择,那么有许多不同的选项可以托管您的数据库。我可以在我的 Linux 机器上通过 DigitalOcean 自行托管,使用 AWS/Azure 等云提供商,或者选择特定于 MongoDB 的 DBaaS 服务。

但我需要一个快速、安全、托管的 MongoDB 解决方案,能够随着我们在这个定制 WordPress 仪表盘中添加的模块数量的增长而轻松扩展。这就是 MongoDB Atlas。
MongoDB Atlas 是一个云托管的 MongoDB 服务,由构建数据库的同一个团队设计和运行。你猜怎么着,我相信他们遵循最佳操作实践,因为他们是最初构建 MongoDB 的人。
我希望这个自定义仪表盘是自我管理的、无服务器的,使用 MongoDB Atlas 可以让我无需担心软件修补、备份以及新数据库更新的可靠配置设置。这又是一个巨大的优势。
此外,MongoDB Atlas 支持跨平台和跨区域,并支持不同的云提供商,这使其成为更好的选择。我认为每个集群都带有两个副本集,随时可以扩展。
MongoDB Compass
既然我们要使用 MongoDB,那么有一个工具可以让我们探索数据库、查看更改、调试等等,那就太好了。为此,MongoDB 再次凭借名为 MongoDB Compass 的产品领先一步。看一看。

我建议您立即下载 MongoDB Compass。它真的是可视化您的 MongoDB 数据库的最佳工具。以下是一些功能:
- 可视化和探索: 看看你的数据库,了解它的样子,甚至可视化地图/坐标等内容。
- 插入、修改和删除: 您还可以直接从 MongoDB Compass 执行数据库的 CRUD 操作。让测试变得更容易。
- 调试和优化: 最后,在数据库的优秀 GUI 中分析您的数据,调试它,甚至找出性能问题。如果您使用 MongoDB,这个工具是必备的。
- 可扩展: 最好的部分是您可以构建自己的插件来扩展 MongoDB Compass。这是关于构建您自己的 Compass 插件的文档。
- 企业版: MongoDB Compass 有几个版本:社区版(免费)和企业版(许可)——企业版允许您可视化数据库模式。
MongoDB Atlas 入门
让我们开始构建一个简单的模块,它是正在构建的定制 WordPress 仪表盘的一部分。对于这个模块,我们正在收集所有与销售相关的数据。为此,我们需要一个 MongoDB 实例,当然我们在这里使用 MongoDB Atlas。
步骤#1:前往 MongoDB Atlas ->
访问 MongoDB Atlas 网站,注册一个完全免费的 MongoDB 实例,托管在 AWS 上,拥有共享 RAM 和 512 Mb 存储空间。点击 免费开始 按钮。
步骤#2:在 MongoDB Atlas 注册 ->
现在继续使用您的电子邮件 ID 注册并填写详细信息。令人惊讶的是,您可以注册并使用免费的 MongoDB Atlas 托管数据库实例,他们甚至不需要您添加信用卡。
步骤#3:创建集群
现在您将被重定向到一个页面,其中包含有关您即将创建的新 MongoDB 集群的大量信息。我建议您查看这些信息,然后点击底部截图所示的 创建集群 按钮继续。
步骤#4:创建数据库用户/密码
大约一分钟后,您的数据库将创建成功。创建完成后,前往 安全 > MongoDB 用户,点击右侧的 + 添加新用户 按钮,为您的数据库创建一个新用户。为了本文的介绍目的,让我们将所有其他设置保持为默认值。
我将用户/密码设置为usermongo
,但您知道更好的方式。
步骤#5:将 IP 添加到白名单以供访问
为了能够访问您的 MongoDB Atlas 数据库,您需要设置 IP 白名单,其中包含您应用程序托管服务器的 IP。身份验证超出了我在此讨论的范围,因此为了本演示的目的,我们只允许所有人(这在生产环境中实际上是不良实践)。
因此,再次前往 安全 > IP 白名单,点击右侧的 + 添加 IP 地址 按钮,最后点击 允许从任何地方访问 按钮以允许匿名访问。
步骤#6:通过 MongoDB Compass 连接
现在我们的数据库 IP 访问和用户都已创建,我们可以获取连接字符串并使用它通过 MongoDB Compass 应用程序连接到我们的数据库。
前往 Connect
,然后选择 Connect with MongoDB Compass,如果还没有下载 Compass,请下载,然后复制 URI 连接字符串。最后,打开 Compass,它应该能够检测到您剪贴板中的连接字符串,允许它连接到您的数据库。
您就可以可视化您的数据库,分析其性能,甚至运行完整的 CRUD 操作了。太棒了!
现在我们已经创建了一个 MongoDB Atlas,并将其与 MongoDB Compass 连接,我们可以继续前进,开始构建我们的 Node.js 应用程序。
WordPress REST API — 真是太棒了!
这个基于 WordPress 的 Node.js 定制仪表盘通过 WordPress REST API 与 WordPress 实例交互。由于这是一个 Node.js 应用程序,我正在使用由 K Adam White 编写的名为 wpapi 的出色库。他还构建了一个基于 express 的 WordPress 演示应用程序。在构建这个定制仪表盘时,我深受启发。所以,您在这里会看到很多它的影子。
基于 Express 的 WordPress 自定义路由
路由器是使用 express 设置的。以下是使用 WordPress 和 express 的基本错误处理程序和路由模板。
'use strict'; var express = require('express'); var router = express.Router(); var siteInfoMiddleware = require('../middleware/site-info'); // Set global site info on all routes router.use(siteInfoMiddleware); // Public Routes // ============= router.get('/', require('./index')); router.get('/page/:page', require('./index')); router.get('/:slug', require('./single')); router.use('/tags/:tag', require('./tag')); router.use('/categories/:category', require('./category')); // Catch 404 and forward to error handler. router.use(function (req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // Error Handling // ============== // Development error handler will print stacktrace. function developmentErrorRoute(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); } // Production error handler. No stacktraces leaked to user. function friendlyErrorRoute(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); } // Configure error-handling behavior if (router.get('env') === 'development') { router.use(developmentErrorRoute); } else { router.use(friendlyErrorRoute); } module.exports = router;
基于 Express 的基本实现
我没有把整个东西托管在 WordPress 上,但最初的计划就是这样做。如果你想这样做,你会想通过使用 RSVP.hash 实用程序来方便地查询所有信息来构建索引,并且
'use strict'; var wp = require( '../services/wp' ); var contentService = require( '../services/content-service' ); var pageNumbers = require( '../services/page-numbers' ); var pageTitle = require( '../services/page-title' ); var RSVP = require( 'rsvp' ); function getHomepage( req, res, next ) { var pages = pageNumbers( req.params.page ); RSVP.hash({ archiveBase: '', pages: pages, title: pageTitle(), // Primary page content posts: wp.posts().page( pages.current ), sidebar: contentService.getSidebarContent() }).then(function( context ) { if ( req.params.page && ! context.posts.length ) { // Invalid pagination: 404 return next(); } res.render( 'index', context ); }).catch( next ); } module.exports = getHomepage;
内置身份验证
对于此设置,您还需要通过向 Node.js 应用程序提供身份验证数据来进行身份验证,该数据可以与 wpapi
一起进行处理,如下所示。请注意,如果您不使用正确的权限和环境变量设置,这并非总是最佳实践。
var WP = require( 'wordpress-rest-api' ); var _ = require( 'lodash' ); var config = _.pick( require( './config' ).wordpress, [ // Whitelist valid config keys 'username', 'password', 'endpoint' ]); var wp = new WP( config ); module.exports = wp;
网站内容聚合
最后,您可以通过创建一个内容服务来获取所有内容,该服务负责递归地获取
- 分页集合的所有页面。
- 您的 WordPress 站点信息。
- 按字母顺序排列的类别列表。
- 内容缓存中特定类别(由 slug 指定)。
- 按字母顺序排列的标签列表。
- 内容缓存中特定标签(由 slug 指定)
- 其他需要与 WordPress 保持功能一致性的内容。
其代码大致如下所示。
'use strict'; var wp = require( './wp' ); var cache = require( './content-cache' ); var _ = require( 'lodash' ); var RSVP = require( 'rsvp' ); /** * Recursively fetch all pages of a paged collection * * @param {Promise} request A promise to a WP API request's response * @returns {Array} A promise to an array of all matching records */ function all( request ) { return request.then(function( response ) { if ( ! response._paging || ! response._paging.next ) { return response; } // Request the next page and return both responses as one collection return RSVP.all([ response, all( response._paging.next ) ]).then(function( responses ) { return _.flatten( responses ); }); }); } function siteInfo( prop ) { var siteInfoPromise = cache.get( 'site-info' ); if ( ! siteInfoPromise ) { // Instantiate, request and cache the promise siteInfoPromise = wp.root( '/' ).then(function( info ) { return info; }); cache.set( 'site-info', siteInfoPromise ); } // Return the requested property return siteInfoPromise.then(function( info ) { return prop ? info[ prop ] : info; }); } /** * Get an alphabetized list of categories * * All archive routes display a sorted list of categories in their sidebar. * We generate that list here to ensure the sorting logic isn't duplicated * across routes. * * @method sortedCategories * @return {Array} An array of category objects */ function sortedCategories() { return all( wp.categories() ).then(function( categories ) { return _.chain( categories ) .sortBy( 'slug' ) .value(); }); } function sortedCategoriesCached() { var categoriesPromise = cache.get( 'sorted-categories' ); if ( ! categoriesPromise ) { categoriesPromise = sortedCategories(); cache.set( 'sorted-categories', categoriesPromise ); } return categoriesPromise; } /** * Get a specific category (specified by slug) from the content cache * * The WP API doesn't currently support filtering taxonomy term collections, * so we have to request all categories and filter them down if we want to get * an individual term. * * To make this request more efficient, it uses sortedCategoriesCached. * * @method categoryCached * @param {String} slug The slug of a category * @return {Promise} A promise to the category with the provided slug */ function categoryCached( slug ) { return sortedCategoriesCached().then(function( categories ) { return _.findWhere( categories, { slug: slug }); }); } /** * Get a specific tag (specified by slug) from the content cache * * The WP API doesn't currently support filtering taxonomy term collections, * so we have to request all tags and filter them down if we want to get an * individual term. * * To make this request more efficient, it uses the cached sortedTags promise. * * @method tagCached * @param {String} slug The slug of a tag * @return {Promise} A promise to the tag with the provided slug */ function tagCached( slug ) { return sortedTagsCached().then(function( tags ) { return _.findWhere( tags, { slug: slug }); }); } /** * Get an alphabetized list of tags * * @method sortedTags * @return {Array} An array of tag objects */ function sortedTags() { return all( wp.tags() ).then(function( tags ) { return _.chain( tags ) .sortBy( 'slug' ) .value(); }); } function sortedTagsCached() { var tagsPromise = cache.get( 'sorted-tags' ); if ( ! tagsPromise ) { tagsPromise = sortedTags(); cache.set( 'sorted-tags', tagsPromise ); } return tagsPromise; } function getSidebarContent() { return RSVP.hash({ categories: sortedCategoriesCached(), tags: sortedTagsCached() }); } module.exports = { // Recursively page through a collection to retrieve all matching items all: all, // Get (and cache) the top-level information about a site, returning the // value corresponding to the provided key siteInfo: siteInfo, sortedCategories: sortedCategories, sortedCategoriesCached: sortedCategoriesCached, categoryCached: categoryCached, tagCached: tagCached, sortedTags: sortedTags, sortedTagsCached: sortedTagsCached, getSidebarContent: getSidebarContent };
自定义路由和销售数据
最后,我添加了相当多的自定义路由,从中可以获取任何类型的销售相关数据。对于我所采用的特定架构,我再次使用 RSVP.hash 实用程序以方便和并行处理。它运行得非常完美。
var WPAPI = require( 'wpapi' ); var RSVP = require('rsvp'); // Using the RSVP.hash utility for convenience and parallelism RSVP.hash({ categories: wp.categories().slug( 'it-services' ), tags1: wp.tags().slug('hotel-name'), tags2: wp.tags().slug('march-events') }).then(function( results ) { // Combine & map .slug() results into arrays of IDs by taxonomy var tagIDs = results.tags1.concat( results.tags2 ) .map(function( tag ) { return tag.id; }); var categoryIDs = results.categories .map(function( cat ) { return cat.id; }); return wp.posts() .tags( tags ) .categories( categories ); }).then(function( posts ) { // These posts are all fiction, either magical realism or historical: console.log( posts ); });
一旦我获得这些数据,我就会将其发送到 Paddle.com 进行处理,同时发送购买订单请求,以便通过无服务器 Azure 函数将其添加到我们的 MongoDB 实例中。
// Registering custom routes. site.itSales = site.registerRoute( 'sales/v1', '/resource/(?P<some_part>\\d+)' ); site.itSales().somePart( 7 ); // => myplugin/v1/resource/7 // Query Parameters & Filtering Custom Routes. site.handler = site.registerRoute( 'sales/v1', 'receipts/(?P<id>)', { // Listing any of these parameters will assign the built-in // chaining method that handles the parameter: params: [ 'before', 'after', 'author', 'parent', 'post' ] }); // Yields from the custom data of buyers. site.handler().post( 8 ).author( 92 ).before( dateObj )... // Sent to paddle.
这可能对某些人来说看起来很奇怪,但 WordPress 允许您设置自定义文章类型和自定义分类法,这就是我在这里使用的。然而,上面的代码不是确切的实现,而是一种与我通过类别和标签使用的方法类似的方法。
这些数据被发送到 Paddle,并且被大量缓存,这样我们的 WordPress 实例在实验自定义仪表盘时就不会受到任何负载。我还加入了一个小型 data-refresh
模块,它可以按需从所选的 WordPress 实例获取数据。
Microsoft Azure & Azure 函数
在构建这个定制的 WordPress 仪表盘时,我希望确保仪表盘的每个模块都以一个无服务器应用程序的形式存在,包含多个无服务器函数。这个决定是基于尽可能降低仪表盘成本的考虑。
三种选择
目前有三大主要的云服务提供商。它们是 Microsoft Azure、Google Cloud Platform 和 Amazon Web Services。它们都提供无服务器函数,分别称为 Azure 函数、GCP Cloud Functions 和 AWS Lambdas。
选择 Azure
Azure 拥有最大的云架构和全球影响力。50 个 Azure 区域,比任何其他云提供商都多,在测试了这三个区域后,我发现 Azure 函数在阿联酋的响应时间最好(因为我的客户的业务总部在阿联酋)。
此外,我们正在使用 Azure ML Studio、AI 认知服务和虚拟机来托管该项目的部分内容,因此使用 Azure 函数来实现无服务器架构是完全合理的。
Azure Functions 入门
让我们开始使用 Azure Functions。我将带您完成创建一个简单的无服务器 Azure 函数的过程,该函数将通过 HTTP 请求触发,并在其中处理从 Paddle.com 发送给我们的销售信息。
我们正在构建什么?!
- 我正在构建一个基于 JavaScript,特别是 Node.js 代码的无服务器 Azure 函数。
- 这个 Azure 函数将通过我们的第三方支付解决方案 Paddle.com 发送的简单
GET
HTTP 请求触发。 - 一旦 Paddle.com 上发生销售,它就会触发一个 webhook,其中包含与我们的销售、数量、商品、收入以及 WordPress 发送给 Paddle 的一些会员相关数据。
- 使用 WordPress REST API,我添加了一些与购买产品的用户相关的自定义数据,例如用户在 WordPress 数据库中的 ID,发生此销售的 WordPress 站点,以及此类用户的元信息。
- 当 Azure 函数收到此
GET
请求时,它会处理信息,提取我需要保存在 MongoDB Atlas 集群中的内容,并形成一个 JavaScript 对象,准备保存到数据库中。 - 然后,Azure 函数通过一个名为 mongoose 的 npm 包连接到 MongoDB Atlas 实例,在连接数据库后,我创建一个数据库模型/模式,然后将此数据保存到 MongoDB Atlas 集群。
- 之后,Azure 函数便在那里等待下一次销售发生,我的客户只需支付 Azure 函数的执行时间和执行次数。(其中每月有 100 万次免费执行)。
现在,这只是对正在发生的事情的概括性总结,我跳过了许多步骤,例如身份验证,这些超出了本文的范围。您应该始终设置身份验证和验证,以保持事情正常运行并避免任何超额使用。
那么,让我们开始构建这个东西吧。
步骤#1:设置 Microsoft Azure 和 VSCode
我希望您已经设置好 Azure 帐户。您需要使用信用卡订阅,因为我们需要存储来托管将用于 Azure Functions 的 Node.js 文件,并且您必须为存储付费(您可能会在第一个月获得 200 美元的免费额度,即使在那之后,费用也相当低)。
所以,请继续设置以下内容
- 使用信用卡在账单中设置 Microsoft Azure 账户。
- 安装 Visual Studio Code (Psst. 我正在制作一个 VSCode 课程)。
- 在您的 VSCode 中安装 Azure Functions 扩展。
- 要启用本地调试,请安装 Azure Functions Core Tools。
- 创建一个新目录并在 VSCode 中打开它。
如果您想知道我正在使用什么主题和字体,它是 Shades of Purple——有关更多信息,请参阅我使用哪些软件和硬件。
步骤#2:创建一个新的函数应用项目
现在让我们创建一个新的函数应用项目。这在 VSCode 中非常容易。您只需前往活动栏中的 Azure 扩展资源管理器。从那里访问 函数 选项卡并点击第一个 创建新项目 图标。
这将创建一个演示项目,其中包含入门所需的基本文件,并为您初始化一个 Git 仓库。我将继续使用基于小 gif 的演示,以便让您更容易理解。
步骤#3:创建一个 HTTP 触发的 Azure 函数
现在我们已经创建了一个函数应用项目,接下来创建 HTTP 触发的无服务器 Azure 函数。为此,请转到活动栏中的 Azure 扩展资源管理器。从那里访问 函数 选项卡,然后点击第二个图标 创建函数。
为了本演示的目的,我选择将身份验证部分保持简单,因此选择匿名访问。我们的 Azure 函数名称是 HttpTriggerJS
,因此您可以在项目内部找到一个以此名称创建的新目录。该目录应包含两个文件,即 functions.json 和 index.js。
函数 是 Azure Functions 中的核心概念。您使用您选择的语言编写函数代码,并将代码和配置文件保存在同一个文件夹中。
配置命名为 function.json,其中包含 JSON 配置数据。它定义了函数绑定和其他配置设置。运行时使用此文件来确定要监视的事件以及如何将数据传入和传出函数执行。在此处官方文档中阅读有关此文件的更多信息。
以下是创建的 function.json 文件的示例。
{ "disabled": false, "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req" }, { "type": "http", "direction": "out", "name": "res" } ] }
然后,有一个 index.js 文件,其中包含您可以用来测试 Azure 函数的基本代码。它接收一个参数 name
并将其打印回给您,或者显示一个错误,要求您提供此参数。
module.exports = function (context, req) { context.log('JavaScript HTTP trigger function processed a request.'); if (req.query.name || (req.body && req.body.name)) { context.res = { // status: 200, /* Defaults to 200 */ body: "Hello " + (req.query.name || req.body.name) }; } else { context.res = { status: 400, body: "Please pass a name on the query string or in the request body" }; } context.done(); };
步骤#4:部署和测试您的 Azure 函数
现在我们已经创建了一个可以通过 GET
HTTP 请求触发的 Azure 函数,让我们继续使用 VSCode 部署它,并使用 Postman API Explorer 进行测试。
要部署该函数,请转到活动栏中的 Azure 扩展资源管理器。从那里访问 函数 选项卡,然后点击第三个图标 部署到函数应用。
这将询问您关于应用程序名称的一系列问题,使用任何独特的名称。我使用了 demo-wp-mdb-azure
— VSCode 随后会使用它创建一个资源组,将您的函数应用程序相关资源、其存储(用于保存文件)以及创建的 Azure 函数分组在一起 — 最后向我们返回一个公共 URL。
然后我继续访问这个 URL,它要求提供 name
参数,正如代码所示,然后当我使用 Postman 应用程序发送 name
参数时,它返回了 Hello Ahmad Awais
。
VSCode 还要求我将函数扩展应用程序版本更新到 Beta 版,我选择了是——因为这会帮助我使用 Node.js v8 进行 async/await。
步骤#5:创建 package.json 并安装 mongoose
现在我们的 Azure 函数已启动并运行。让我们在项目的根目录中创建一个 package.json 文件并安装 mongoose。我们需要它来连接并保存数据到我们的 MongoDB Atlas 集群。
Mongoose 提供了一个直接的、基于模式的解决方案来建模您的应用程序数据。它开箱即用地包含了内置的类型转换、验证、查询构建、业务逻辑钩子等等。它非常棒。
步骤#6:添加 MongoDB 连接的应用设置
现在我们几乎准备好开始编写应用程序代码了。但在那之前,我们需要一个连接字符串才能连接到我们的 MongoDB Atlas 集群(就像我们使用 MongoDB Compass 所做的那样)。此连接字符串是私密的,您不应将其提交到 Git 仓库。
此连接字符串属于项目根目录下的 local.settings.json 文件。让我们首先下载设置,然后添加带有我们的连接字符串(从 MongoDB Atlas 仪表盘获取此字符串)的 MongodbAtlas
设置,并上传应用程序设置。
要执行此操作,请转到活动栏中的 Azure 扩展资源管理器。从那里访问 函数 选项卡并选择您的订阅,然后选择您的 Azure 函数应用,即 demo-wp-mdb-azure
,然后右键单击 应用程序设置 以选择 下载远程设置... 进行下载,并在添加 MongodbAtlas
连接字符串后选择 上传本地设置...
进行上传。
步骤#7:更新 Azure 函数的 Node 版本
在代码中,我打算使用 async
/await
,它们在 Azure Functions 默认版本 1 附带的 Node.js v6.5.0
中不可用。在步骤 #4 中,VSCode 要求我将 Azure Functions 的运行时版本更新到 Beta 版,我照做了。这使 Azure Functions 支持最新的 Node.js 版本。
因此,让我们只需更新本地设置中的 WEBSITE_NODE_DEFAULT_VERSION
应用程序设置,并将其更新到远程设置。
步骤#8:创建 MongoDB 模型/模式
在我们将任何数据保存到 MongoDB Atlas Cluster 之前,让我们创建一个 modelSale.js 文件,其中包含我们打算保存到数据库中的模型的模式。这是一个极其简单的模式实现,我建议您阅读有关使用 mongoose 和 MongoDB 可以在此处执行的操作。
此文件基本上是自解释的。
/** * Model: Sale */ const mongoose = require('mongoose'); mongoose.Promise = global.Promise; // Sale Schema. const saleSchema = new mongoose.Schema({ sale_gross: Number, earnings: Number, currency: String, memberSince: Date, customerEmail: String, event_time: { type: Date, default: Date.now }, }); // Export the model. module.exports = mongoose.model('Sale', saleSchema);
步骤#9:使用 Node.js 编写 Azure 函数代码
现在让我们来编写 Azure 函数的代码。为了本演示的目的,我将所有主要代码都放在 index.js 文件中。另外,我将使用 context 对象 作为第一个参数,请务必阅读相关内容。其他一切都在下面的代码片段中解释。
所以,这只是本文的演示代码。它执行以下操作:
- 从 Paddle.com 获取数据。
- 通过我们在应用程序设置中添加的连接字符串连接到 MongoDB Atlas。
- 使用
test
数据库中定义的数据库模式,在该数据库中创建一个sales
集合,其中包含我们的销售文档。 - 验证数据并创建
finalData
对象,该对象将保存到 MongoDB Atlas 集群中。太棒了!!! - 最后,如果一切顺利,向 Paddle webhook 响应
200
状态码,并执行 context.done() 操作。
所有内容都已通过内联文档进行了详细解释。
/** * Azure Function: Sale. * * Gets data from Paddle.com (which in turn gets data * from WordPress) and processes the data, creates a * finalData object and saves it in MongoDB Atlas. * * @param context To pass data between function to / from runtime. * @param req HTTP Request sent to the Azure function by Paddle. */ module.exports = async function (context, req) { // Let's call it log. const log = context.log; // Log the entire request just for the demo. log('[RAN] RequestUri=%s', req.originalUrl); /** * Azure function Response. * * Processes the `req` request from Paddle.com * and saves the data to MongoDB Atlas while * responding the `res` response. */ // Database interaction. const mongoose = require('mongoose'); const DATABASE = process.env.MongodbAtlas; // Connect to our Database and handle any bad connections mongoose.connect(DATABASE); mongoose.Promise = global.Promise; // Tell Mongoose to use ES6 promises mongoose.connection.on('error', (err) => { context.log(`ERROR→ ${err.message}`); }); // Sale Schema. require('./modelSale'); const Sale = mongoose.model('Sale'); // Create a Response. if (req.query.customFieldName) { // Simple authentication for the purpose of demo. // Build the data we need. const sale_gross = req.query.p_sale_gross || '0'; const earnings = JSON.parse(req.query.p_earnings)['16413'] || '0' const currency = req.query.p_currency || 'USD'; const memberSince = req.query.memberSince || new Date(); const customerEmail = req.query.customerEmail || ''; const event_time = new Date(); log('[OUTPUT]—— sale_gross: ' + sale_gross); log('[OUTPUT]—— earnings: ' + earnings); log('[OUTPUT]—— currency: ' + currency); const finalData = { sale_gross: sale_gross, earnings: earnings, currency: currency, memberSince: memberSince, customerEmail: customerEmail, event_time: event_time, } // Save to db. const sale = await (new Sale(finalData)).save(); log("[OUTPUT]—— SALE SAVED: ", sale); // Respond with 200. context.res = { status: 200, body: "Thank You for the payment! " + (req.query.customFieldName || req.body.customFieldName) }; } else { context.res = { status: 400, body: "Please pass a name on the query string or in the request body" }; } // Informs the runtime that your code has finished. You must call context.done, or else the runtime never knows that your function is complete, and the execution will time out. // @link: https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node#contextdone-method context.done(); };
步骤#10:重新部署 Azure 函数
现在让我们重新部署 Azure 函数。为此,请转到活动栏中的 Azure 扩展资源管理器。从那里访问 函数 选项卡,然后点击第三个 部署到函数应用 图标。
步骤#11:通过 Paddle 的 Webhook 测试 Azure 函数
看起来我们差不多完成了。剩下的就是通过 Paddle.com 触发一个虚拟 webhook 来测试我们的 Azure 函数。让我们开始吧。另外,当一切正常时,让我们探索一下我们的数据在 MongoDB Compass 中是什么样子。
哇,天哪!!!真不容易。很高兴它成功了。
那么,发生了什么?!
准备好听一长串内容吧。我正在构建的定制 WordPress 仪表盘应用程序中,我创建了 Sales
模块的一小部分。我使用了 MongoDB Atlas 和 Compass,然后通过 VSCode 使用函数应用创建了 Microsoft Azure 函数,使用环境变量秘密作为应用程序字符串和 MongoDB 连接字符串部署了应用程序,更新了 Node.js 版本,并通过来自 Paddle.com 的虚拟 webhook(就像销售发生时触发一样)触发了函数,将数据(来自 Paddle + WordPress)发送到我们的 Azure 函数,然后从那里发送到 MongoDB Atlas。而且它成功了,哈哈!
机器学习与人工智能
机器学习和人工智能在软件技术领域始终是一个迷人的话题,但我们在 WordPress 或 WordPress 社区中很少谈论它。
我着手通过为我的客户选择的少数几个 WordPress 网站添加一些小的改进来改变这一点,并且完全打算在这个定制的 WordPress 仪表盘中探索同样的可能性。
我之前讨论过这个话题,并分享了我正在做什么,看看我正在为 WordPress 构建的这个小型人工智能插件,并将其与不同的 Azure 认知服务集成。
我在另一篇文章的视频中解释了这一点,您可以在这里找到:构建一个 WordPress 人工智能插件 ->
我通过 wpapi
包在这个仪表盘中也取得了类似的结果。首先我将图像上传到认知服务,然后在获得自信的响应后,我将其发送到 WordPress,通过 WordPress REST API 上传,并附带由计算机视觉人工智能生成的图像描述。
/** * Get Image Alt Recognition with Computer Vision * using Azure Cognitive Services. */ var WPAPI = require('wpapi'); var wp = new WPAPI({ endpoint: 'http://src.wordpress-develop.dev/wp-json' }); /** * Handle Image Alt Generation. */ function processImage() { // ********************************************** // *** Update or verify the following values. *** // ********************************************** // Replace <Subscription Key> with your valid subscription key. var subscriptionKey = "<Subscription Key>"; // You must use the same region in your REST call as you used to get your // subscription keys. For example, if you got your subscription keys from // westus, replace "westcentralus" in the URI below with "westus". // // Free trial subscription keys are generated in the westcentralus region. // If you use a free trial subscription key, you shouldn't need to change // this region. var uriBase = "https://westcentralus.api.cognitive.microsoft.com/vision/v2.0/analyze"; // Request parameters. var params = { "visualFeatures": "Categories,Description,Color", "details": "", "language": "en", }; // Display the image. var sourceImageUrl = document.getElementById("inputImage").value; document.querySelector("#sourceImage").src = sourceImageUrl; // Make the REST API call. $.ajax({ url: uriBase + "?" + $.param(params), // Request headers. beforeSend: function (xhrObj) { xhrObj.setRequestHeader("Content-Type", "application/json"); xhrObj.setRequestHeader( "Ocp-Apim-Subscription-Key", subscriptionKey); }, type: "POST", // Request body. data: '{"url": ' + '"' + sourceImageUrl + '"}', }) .done(function (data) { // Show formatted JSON on webpage. $("#responseTextArea").val(JSON.stringify(data, null, 2)); // Extract and display the caption and confidence from the first caption in the description object. if (data.description && data.description.captions) { var caption = data.description.captions[0]; if (caption.text && caption.confidence >= 0.5) { const imgDescription = caption.text; // Upload to WordPress. wp.media() // Specify a path to the file you want to upload, or a Buffer .file(sourceImageUrl) .create({ title: imgDescription, alt_text: imgDescription, caption: imgDescription, description: imgDescription }) .then(function (response) { // Your media is now uploaded: let's associate it with a post var newImageId = response.id; return wp.media().id(newImageId).update({ post: associatedPostId }); }) .then(function (response) { console.log('Media ID #' + response.id); console.log('is now associated with Post ID #' + response.post); }); } } }) .fail(function (jqXHR, textStatus, errorThrown) { // Display error message. var errorString = (errorThrown === "") ? "Error. " : errorThrown + " (" + jqXHR.status + "): "; errorString += (jqXHR.responseText === "") ? "" : jQuery.parseJSON(jqXHR.responseText).message; alert(errorString); }); };
内容审核自动化
我们有一个想法是利用 Azure 的 AI/ML 作为内容审核平台,它提供内置的人工干预 + 机器学习来帮助审核图像、文本和视频。这正在进行中,但它是一个非常有趣的东西,您绝对应该看看。
WordPress 语法(纳粹)智能
你们都有一遍又一遍地输入错别字的习惯。我一直都是这样。最酷的事情是像 Bing 和 Google 这样的搜索引擎可以为您拼写检查和校对搜索查询。
如果 WordPress 有这个功能呢?!——于是,我开始着手,最终在 WordPress 后台区域实现了同样的功能,当你在文章标题中输入一个错别字或多个错别字时,我都很关心。
我太激动了,无法控制自己,所以我在左下角。非常开心和惊喜!
现在轮到你了!
我真的希望您喜欢所有这些现代 JavaScript 框架、AI/ML 产品和无服务器功能之间的潜在集成。
这个项目非常有趣。我认为如果你尝试一下这个技术栈,你也可以享受到这种疯狂的乐趣。所以,我把它留给你们去尝试 MongoDB Atlas,在 WordPress 的背景下——也许把它连接到一堆无服务器函数。
如果您能在 Twitter 上分享此帖子 ->,那对我意义重大。
也欢迎随时在 Twitter @MrAhmadAwais 与我打招呼 👋。
祝好!