站長資訊網
        最全最豐富的資訊網站

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

        本篇文章給大家分享一個實戰,介紹一下在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<reportAnalytics.length; i++) {         if (reportAnalytics[i].ip !== ip) {            return ('No Coordinates found with that IP');         };     }

        在上面的代碼中,我們使用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<coordinatesArray.length; i++) {         if (i == coordinatesArray.length - 1) {             break;         }         let distance = calculateDistance(coordinatesArray[i], coordina         tesArray[i+1]);         totalLength += distance;     }

        在上面的代碼中,totalLength 代表從兩個坐標計算出來的總距離。為了遍歷coordinatesArray ,我們需要初始化我們的計算結果。將totalLength 設置為零,初始化總距離。

        第二行包含我們使用的迭代代碼forloop 。我們用let i=0 來初始化i 變量。i 變量代表當前元素在coordinatesArray 的索引。

        i<coordinatesArray.length 設置迭代的條件,只有當當前元素的索引小于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);     } }

        在控制器文件中添加錯誤處理

        Require appError.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<reportAnalytics.length; i++) {         if (reportAnalytics[i].ip !== ip) {            return next(new AppError('No Coordinates found with that IP', 404));         };     }

        接下來,在你的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 文件中的代碼應該看起來像下面的截圖。

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

        storeController.js文件的屏幕截圖

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

        fetchController.js文件第1-32行

        一文詳解如何在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 文件應該看起來像下面的屏幕截圖。

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

        app.js文件

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

        "start:dev": "node server.js",     "debug": "ndb server.js"

        你的package.json 文件應該看起來像下面的截圖。

        一文詳解如何在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

        你會在你的終端看到以下輸出。

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

        終端

        我們的API托管在Heroku上,它的最終輸出應該看起來像下面的輸出。

        一文詳解如何在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.

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 亚洲国产精品成人精品无码区 | 中文字幕亚洲精品无码| 99精品免费视品| 亚洲国产精品无码久久久蜜芽| 四虎影视国产精品亚洲精品hd | 久久夜色精品国产噜噜亚洲AV| 亚洲精品无码高潮喷水在线| 国产精品一香蕉国产线看观看| 人妻少妇看A偷人无码精品| 亚洲AV无码之日韩精品| 午夜不卡久久精品无码免费 | 一本之道av不卡精品| 精品久久久久久99人妻| 午夜成人精品福利网站在线观看| 在线亚洲精品福利网址导航| 国产精品综合久久第一页| 精品国产美女福利到在线不卡| 亚洲欧美日韩精品| 国产综合精品一区二区三区| 国产午夜精品一区理论片| 国产精品麻豆欧美日韩ww| 久久99精品久久久久久9蜜桃| 久久精品国产精品亚洲精品| 国产三级精品久久| 欧美精品一区二区精品久久| 99久久婷婷免费国产综合精品| 无码人妻精品一区二区三区66 | 日韩国产成人精品视频| 亚洲av永久无码精品表情包| 亚洲精品麻豆av| 一区二区日韩国产精品| 最新在线精品国自av| 亚洲精品午夜无码专区| 亚洲精品国产精品乱码视色| 亚洲精品卡2卡3卡4卡5卡区| 自拍偷在线精品自拍偷| 国产精品美女久久久| 国产成人精品优优av| 国产在线精品一区免费香蕉| 午夜精品久久久久久毛片| 四虎成人精品无码|