一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

青灯夜游
發布: 2022-02-24 19:27:17
轉載
1151 人瀏覽過

這篇文章跟大家分享一個實戰,介紹一下在Node中建立一個位置分析報告API的方法,在本教程結束時,你也會對Node.js中的錯誤處理和好的文件結構有更好的理解,希望對大家有幫助!

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

由經緯度定義的位置,可以與其他資料結合使用,為企業產生洞察力,這就是所謂的位置分析。

在全球經營的企業在整個價值鏈中使用位置分析,例如,用於定位用戶、提供服務和運行目標廣告。隨著社群媒體和行動裝置的興起,位置分析的使用在全球範圍內增加。

在本教程中,我們將學習如何在Node.js中建立一個輕量級的位置分析報告服務API。在本教程結束時,你將能夠為自己的專案建立這種類型的API。你也會對Node.js中的錯誤處理和良好的檔案結構有更好的理解

讓我們開始吧!

前提條件

要繼續學習本教程,你需要具備以下條件。

  • 熟悉Node.js、Express和Git
  • Visual Studio程式碼編輯器
  • Heroku帳號
  • Postman帳號

#設定檔案結構

首先,我們需要設定我們的檔案結構。打開你的終端,建立一個新的目錄,你將在其中儲存項目的所有檔案。在你的終端,鍵入以下指令,後面跟著資料夾的名稱,lars

mkdir lars
登入後複製

在VS程式碼編輯器中開啟lars工作目錄。

code .
登入後複製

你會看到你的VS Code視窗開啟。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

Visual Studio Code 視窗

透過在Visual Studio中開啟你的終端並執行npm init -y,初始化工作目錄。

如果你想在VS Code之外的作業系統的終端機中執行這個命令,請導航到lars目錄並執行下面的命令。

npm init -y
登入後複製

上面的程式碼自動產生了package.json檔案。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

VS Code顯示已建立的package.json檔案

在本教學中,我們將使用Express作為一個相依性。透過執行下面的命令來安裝Express。

npm install express --save
登入後複製

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

將Express作為依賴關係安裝的命令

#安裝Express後,你會注意到一個node_modules資料夾被創建了。為了確認你已經安裝了Express,請檢查你的package.json文件,你會看到Express被安裝為一個依賴項。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

node_modules資料夾被創建,Express被加入到package.json中。

我們需要將Express導入我們的應用程序,因為它是一個npm模組。在與你的package.json檔案相同的目錄下建立一個名為app.js的新檔案。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

VS程式碼視窗的截圖顯示app.js已經建立。

在你的app.js檔案中,透過執行下面的程式碼requireExpress。

const express = require('express');
登入後複製

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

匯入Express

現在,呼叫Express來建立你的應用程式、路由和你的應用程式要執行的連接埠。

const app = express();
登入後複製

Node.js實作了模組化,這意味著它將你的應用程式分成模組,或各種文件,並匯出每個文件。我們將使用export關鍵字匯出app

module.exports = app;
登入後複製

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

app.js檔案

接下來,在與app.js檔案相同的目錄下建立另一個名為server.js的檔案。Requireapp.js檔案匯入server.js檔案。

const app = require('./app');
登入後複製

在與server.js相同的目錄中建立一個名為config.env的檔案。config.env檔案將包含我們的應用程式所需的所有[process.env](https://nodejs.org/dist/latest-v8.x/docs/api/process. html)我們的應用程式所需的所有金鑰。在config.env檔案中,建立一個PORT變量,並將PORT設定為監聽埠8000

PORT=8000
登入後複製

导入应用程序后,在server.js文件中创建一个名为port的常量。将其设置为你刚刚创建的PORT变量和一个默认的端口3000

const port = process.env.PORT || 3000;
登入後複製

最后,我们将用.listen()方法设置应用程序在该端口上监听。

app.listen(port, () => { console.log(`App listening on ${port}`) });
登入後複製

构建路由

每当你访问一个网页或一个在网络上运行的应用程序时,你都在发出一个HTTP请求。服务器用来自后台或数据库的数据进行响应,这就是所谓的HTTP响应。

当你在一个网络应用程序上创建一个资源时,你正在调用POST请求。同样地,如果你试图删除或更新一个Web应用上的资源,你正在调用DELETEPATCH、或UPDATE请求。让我们建立路由来处理这些请求。

在你的工作目录中创建一个名为routes的文件夹,并在其中创建一个名为analyticsRoute.js的文件。RequireanalyticsRoute.js文件中表达,以设置API的路由。

const express = require('express');
登入後複製

我们还需要从app.js文件中require我们的应用程序模块。

const app = require('../app');
登入後複製

然后,我们创建我们的路由。

const router = express.Router();
登入後複製

最后,我们要导出路由器。

module.exports = router;
登入後複製

建立控制器

我们需要为控制器创建文件,将其导入我们的analyticsRoutes文件。首先,在你的工作目录中创建一个名为controllers的文件夹。

我们的API将使用用户提供的IP地址和坐标来计算距离和位置。我们的请求需要接受这些信息和来自用户的请求。

我们将使用一个POST请求,因为用户在req.body。为了保存这些信息,我们需要在控制器中require一个fs模块(文件系统)。

处理POST的请求

controllers文件夹中创建一个名为storeController.js的文件。在storeController.js文件中,我们需要导入fs模块和fsPromises.readFile()方法来处理返回的promise,也就是用户的IP地址和坐标。

要安装fs模块,在你的工作目录中打开你的终端,运行以下命令。

npm i fs --save
登入後複製

在你的文件顶部输入以下代码。

const fsp = require('fs').promises; const fs = require('fs');
登入後複製
登入後複製

接下来,我们将创建一个控制器,处理我们的POST请求的路由。我们将使用exports关键字并创建一个接受三个参数的异步中间件函数

  • req: 代表请求对象
  • res: 代表响应对象
  • next: 函数在中间件输出后立即被调用。
postAnalytics = async(req, res, next) => {}
登入後複製

现在,我们将把req.body中的数据对象的属性保存到reportAnalytics数组中。我们将设置一个Date()对象,将任何数据的创建日期保存在一个createdAt关键中。

reportAnalytics.push({...req.body, createdAt: new Date()});
登入後複製

我们将创建一个名为storeAnalytics.json的文件,使用JSON.stringify(),将我们的reportAnalytics数组的内容保存为一个字符串。

await fsp.writeFile(`${__dirname}/storeAnalytics.json`, JSON.stringify(reportAnalytics));
登入後複製

当用户提出POST要求时,我们需要检查storeAnalytics.json文件是否存在。如果该文件存在,我们需要读取该文件并保存输出。

输出包含一个名为reportFile的常量,它存储了被读取的文件内容。在reportFile,使用JSON.parse,将文件的内容转换为一个JavaScript对象。

// checks if file exists if (fs.existsSync(`${__dirname}/storeAnalytics.json`)) { // If the file exists, reads the file const reportFile = await fsp.readFile(`${__dirname}/storeAnalytics.json`, 'utf-8') // converts the file to JavaScript Object reportAnalytics = JSON.parse(reportFile) } else { // if file does not exist return ('File does not exist'); }
登入後複製

[fs.existsSync()](https://www.geeksforgeeks.org/node-js-fs-existssync-method/)方法同步地检查文件是否存在。它接受${__dirname}/storeAnalytics.json路径作为其单一参数,并指向我们要检查的文件的位置。

我们将await关键字与reportFile,以等待用fsp.readFile()方法读取文件的结果。接下来,我们用(${__dirname}/storeAnalytics.json来指定我们要读取的文件的路径。我们将编码格式设置为utf-8,这将把从文件中读取的内容转换为一个字符串。

JSON.parse()reportFile转换为JavaScript对象,并将其存储在reportAnalytics数组中。else语句块中的代码只有在文件不存在时才会运行。最后,我们使用了return语句,因为我们想在代码运行后停止函数的执行。

如果文件被成功读取、创建并保存在storeAnalytics.json,我们需要发送一个响应。我们将使用响应对象(res),它是我们的异步postAnalytics函数的第二个参数。

res.status(201).json({ status: 'success', data: { message: 'IP and Coordinates successfully taken' } })
登入後複製

我们将用一个状态success和数据信息IP and Coordinates successfully taken来响应。

你的storeController.js文件应该看起来像下面的屏幕截图。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

处理GET的请求

我们需要创建另一个控制器文件来处理我们的GET请求。当用户向API发出GET请求时,我们将根据他们的IP地址和坐标来计算他们的位置。

controllers文件夹中创建一个名为fetchController.js的文件。fsstoreController.js文件中,我们需要require模块和fsPromises.readFile()方法来处理返回的promise

const fsp = require('fs').promises; const fs = require('fs');
登入後複製
登入後複製

让我们创建控制器来处理我们对GET请求的路由。我们将使用类似的中间件函数和参数来处理上面的POST请求。

exports.getAnalytics = async(req, res, next) => {}
登入後複製

getAnalytics中间件中,输入以下代码,从请求的查询中获得IP地址。

const { ip } = req.query;
登入後複製

现在,创建一个空数组,用来存储req.body的内容。

let reportAnalytics = [];
登入後複製

正如我们之前所做的,我们需要检查storeAnalytics.json文件是否存在。如果文件存在,我们将在reportFile上使用JSON.parse,将文件内容转换为一个JavaScript对象。

if (fs.existsSync(`${__dirname}/storeAnalytics.json`)) { const reportFile = await fsp.readFile(`${__dirname}/storeAnalytics.json`, 'utf-8') reportAnalytics = JSON.parse(reportFile) } else { return ('File does not exist'); }
登入後複製

现在,我们可以在storeAnalytics.json文件中保存用户的IP地址和坐标。任何时候用户请求根据提供的坐标计算地理位置,IP地址将以查询的形式包含在请求中。

现在我们已经从req.query对象中得到了IP地址,我们可以编写代码来检查req.query对象中提供的IP地址是否与存储在storeAnalytics.json文件中的IP地址相同。

for (let i=0; i
        
登入後複製

在上面的代码中,我们使用forloop来循环浏览reportAnalytics数组。我们将变量i,代表当前元素在reportAnalytics数组中的索引,初始化为0。如果i小于reportAnalytics数组的长度,我们将其递增。

接下来,我们检查reportAnalytics数组的IP地址属性是否等于req.query中提供的IP地址。

让我们计算一下只在最后一小时内存储的IP地址的位置。

const hourAgo = new Date(); hourAgo.setHours(hourAgo.getHours()-1); const getReport = reportAnalytics.filter(el => el.ip === ip && new Date(el.createdAt) > hourAgo )
登入後複製

在上面的代码块中,我们创建了一个名为hourAgo的常量,并将其设置为一个Date对象。我们使用setHours()方法将hourAgo设置为最后一个小时的getHours()-1

reportAnalytics文件中的当前IP地址等同于或等于req.query中传递的IP地址时,意味着数据是在最后一小时内创建的,getReport创建一个常量,设置为一个新的数组。

创建一个名为coordinatesArray的常量,它将只存储已经保存在getReport数组中的坐标。

const coordinatesArray = getReport.map(element => element.coordinates)
登入後複製

接下来,我们需要用坐标计算出位置。我们需要遍历coordinatesArray,通过传入保存为坐标的两个值来计算位置。

let totalLength = 0; for (let i=0; i
        
登入後複製

在上面的代码中,totalLength代表从两个坐标计算出来的总距离。为了遍历coordinatesArray,我们需要初始化我们的计算结果。将totalLength设置为零,初始化总距离。

第二行包含我们使用的迭代代码forloop。我们用let i=0来初始化i变量。i变量代表当前元素在coordinatesArray的索引。

i设置迭代的条件,只有当当前元素的索引小于coordinatesArray的长度时才运行。接下来,我们在迭代中增加当前元素的索引,以移动到下一个元素,i++

接下来,我们将检查当前元素的索引是否等于数组中最后一个元素的编号。然后,我们暂停迭代代码的执行,用break关键字移动到下一个。

最后,我们创建一个名为calculateDistance的函数,接受两个参数,即第一和第二坐标值(经度和纬度)。我们将在另一个模块中创建calculateDistance,并将其导出到fetchController.js文件中,然后我们将最终结果保存在我们初始化的totalLength变量中。

注意,每个请求都需要一个响应。我们将用一个200statusCode和一个包含我们将计算的距离值的JSON来响应。只有在代码成功的情况下才会显示响应。

res.status(200).json({distance: totalLength})
登入後複製

你的fetchController.js文件应该看起来像下面两个代码块。

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

fetchController.js文件

一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

fetchController.js文件的续篇

建立calculateDistance函数

在你的工作目录中,创建一个名为utilities的新文件夹,在里面创建一个名为calculateDistance.js的文件。打开calculateDistance.js文件,添加以下函数。

const calculateDistance = (coordinate1, coordinate2) => { const distance = Math.sqrt(Math.pow(Number(coordinate1.x) - Number(coordinate2.x), 2) + Math.pow(Number(coordinate1.y) - Number(coordinate2.y), 2)); return distance; } module.exports = calculateDistance;
登入後複製

在第一行,我们创建一个名为calculateDistance的函数,它接受两个参数:coordinate1coordinate2。它使用下面的方程式。

  • Math.sqrt: 数学中的平方根
  • Math.pow:将一个数字提高到一个幂值
  • Number(): 将一个值转换为一个数字
  • coordinate1.x:第一个坐标(经度)的第二个值
  • coordinate2.x:第一个坐标的第一个值(经度)。
  • coordinate1.y:第二个坐标的第二个值(纬度)。
  • coordinate2.y:第二个坐标的第一个值(纬度)。

现在我们已经创建了calculateDistance函数,我们需要将该函数require到我们fetchController.js文件的代码中。在fs模块之后添加下面的代码。

const calculateDistance = require('../utilities/calculateDistance');
登入後複製

实现错误处理

实现错误处理是很重要的,以防止我们的代码失败或某个特定的实现没有按照设计的方式工作。我们将在开发和生产中添加错误处理。

打开你的config.env文件,运行NODE_ENV=development,将环境设置为开发。

在你的controllers文件夹中,创建一个名为errorController.js的新文件。下面的代码片断创建了一个名为sendErrorDev的函数,以处理在开发环境中遇到的错误。

const sendErrorDev = (err, res) => { res.status(err.statusCode).json({ status: err.status, error: err, message: err.message, stack: err.stack, }); }
登入後複製

我们将创建一个名为sendErrorDev的函数,它接受两个参数,err表示错误,res表示响应。response.status接收错误的statusCode,并以JSON数据进行响应。

此外,我们将创建一个名为sendErrorProd的函数,它将处理API在生产环境中遇到的错误。

const sendErrorProd = (err, res) => { if(err.isOperational) { res.status(err.statusCode).json({ status: err.status, message: err.message }); } else { console.error('Error', err); res.status(500).json({ status: 'error', message: 'Something went wrong' }) } }
登入後複製

在你的utilities文件夹中,创建一个名为appError.js的文件,并输入以下代码。

class AppError extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error'; this.isOperational = true; Error.captureStackTrace(this, this.constructor); } } module.exports = AppError;
登入後複製

我们将创建一个名为AppError的类,它扩展了Error对象。

然后,我们将创建一个构造函数,它将初始化该类的对象。它接受两个参数,叫做messagestatusCodesuper方法用一个参数调用构造函数,将其传入message,并获得对构造函数的属性和方法的访问。

接下来,我们将构造函数的statusCode属性设置为statusCode。我们将构造函数的status属性设置为任何以4开始的statusCode,例如,将404 statusCode设置为failerror

创建另一个名为catchAsync.js的文件,并在其中添加以下代码。

module.exports = fn => { return (req, res, next) => { fn(req, res, next).catch(next); } }
登入後複製

在控制器文件中添加错误处理

RequireappError.js文件和catchAsync.js文件在你的storeController.jsfetchController.js文件中。将这两条导入语句放在两个文件中的代码顶部。

const catchAsync = require('../utilities/catchAsync'); const AppError = require('../utilities/appError');
登入後複製

storeController.jsfetchController.js文件中,用catchAsync()方法包装你的函数,如下所示。

// For storeController.js file exports.postAnalytics = catchAsync(async(req, res, next) => {...} // For fetchController.js file exports.getAnalytics = catchAsync(async(req, res, next) => {...}
登入後複製

接下来,在你的fetchController.js文件中,运行AppError类。

for (let i=0; i
        
登入後複製

接下来,在你的storeController.js文件中运行AppError类。

if (fs.existsSync(`${__dirname}/storeAnalytics.json`)) { const reportFile = await fsp.readFile(`${__dirname}/storeAnalytics.json`, 'utf-8') reportAnalytics = JSON.parse(reportFile) } else { return next(new AppError('File does not exist', 404)); }
登入後複製

你的storeController.jsfetchController.js文件中的代码应该看起来像下面的截图。

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

storeController.js文件的屏幕截图

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

fetchController.js文件第1-32行

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

fetchController.js文件第33-37行

设置验证

我们需要验证在req.body,其中包括IP地址和坐标的数据,是正确的,而且格式正确。坐标应该至少有两个值,代表经度和纬度。

utilities文件夹中,创建一个名为Validation的新文件夹。在Validation文件夹中,创建一个名为schema.js的文件。schema.js文件将包含req.body中提供的任何数据的所需格式。我们将使用[joi](https://www.npmjs.com/package/joi)验证器。

npm install joi
登入後複製

schema.js文件中输入以下代码。

const Joi = require('joi'); const schema = Joi.object().keys({ ip: Joi.string().ip().required(), coordinates: Joi.object({ x: Joi.number().required(), y: Joi.number().required() }).required() }) module.exports = schema;
登入後複製

joi在上面的代码块中,我们require验证器,用它来创建我们的模式。然后,我们将IP地址设置为总是一个字符串,并通过在请求体中要求它来验证IP地址。

我们将坐标设置为object。我们将代表经度和纬度值的xy值都设置为数字,并将其require,以便我们的代码运行。最后,我们导出了模式。

在验证器文件夹中,创建另一个名为validateIP.js的文件。在里面,我们将编写代码来验证IP地址,使用[is-ip](https://www.npmjs.com/package/is-ip)npm包。让我们把这个包导出到我们的代码中。

validateIP.js文件中,添加以下代码。

const isIp = require('is-ip'); const fsp = require('fs').promises; const fs = require('fs'); exports.validateIP = (req, res, next) => { if(isIp(req.query.ip) !== true) { return res.status(404).json({ status: 'fail', data: { message: 'Invalid IP, not found.' } }) } next(); }
登入後複製

运行以下命令,为我们的API安装必要的依赖项。

npm install body-parser cors dotenv express fs is-ip joi morgan ndb nodemon
登入後複製

你的app.js文件应该看起来像下面的屏幕截图。

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

app.js文件

在你的package.json文件中的scripts部分下,添加以下代码片段。

"start:dev": "node server.js", "debug": "ndb server.js"
登入後複製

你的package.json文件应该看起来像下面的截图。

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

package.json文件

用以下代码更新你的analyticsRoute.js文件。

const express = require('express'); const app = require('../app'); const router = express.Router(); const validateIP = require('../utilities/Validation/validateIP'); const storeController = require('../controllers/storeController'); const fetchController = require('../controllers/fetchController'); router.route('/analytics').post(storeController.postAnalytics).get(validateIP.validateIP, fetchController.getAnalytics); module.exports = router;
登入後複製

现在,我们已经完成了我们的位置分析API的构建!现在,让我们测试一下我们的代码,以确保它的工作。

测试API

我们将使用Postman来测试我们的API。让我们启动我们的API以确保它在我们的终端中运行。

node server.js
登入後複製

你会在你的终端看到以下输出。

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

终端

我们的API托管在Heroku上,它的最终输出应该看起来像下面的输出。

1一文詳解如何在Node中建立一個輕量級的位置分析報告服務API

你可以自己在托管的文档中测试这个API

https://documenter.getpostman.com/view/13856921/TzXumeXS

结论

位置分析是企业的一个伟大工具。位置信息可以让公司更好地服务于潜在客户和现有客户。

在本教程中,我們學會了建立一個工具,以IP位址和座標的形式取得位置資訊並計算出距離。我們在Node.js中設定了我們的檔案結構,建立了處理GETPOST請求的路由,新增了錯誤處理,最後測試了我們的應用程式。

你可以使用本教學所學到的資訊來建立你自己的位置報告API,你可以依照自己的業務需求進行客製化。

The postBuild a location analytics reporting API in Node.jsappeared first onLogRocket Blog.

更多node相關知識,請訪問:nodejs 教學

以上是一文詳解如何在Node中建立一個輕量級的位置分析報告服務API的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!