構建node.js基礎鏡像
by Aurélien Giraud
通過AurélienGiraud
在Android上構建Node.js應用程序 (Building a Node.js application on Android)
第2部分:Express和NeDB (Part 2: Express and NeDB)
In Part 1 we saw how to use Termux, a Terminal emulator and Linux environment for Android. We also edited files with Vim and saw how to run Node. We are now going to build a small node application with the Express framework and use NeDB for the database.
在第1部分中,我們了解了如何使用Termux,這是用于Android的終端仿真器和Linux環境。 我們還使用Vim編輯了文件,并了解了如何運行Node。 現在,我們將使用Express框架構建一個小型節點應用程序,并將NeDB用于數據庫。
這個故事,以及誰可以從中受益 (The story, and who could benefit from it)
When I found out I could build a full Node.js application with a Mongo-like database on my Android tablet, I got really excited. So I decided to give it a try, and share about my experience.
當我發現可以在Android平板電腦上使用類似于Mongo的數據庫構建完整的Node.js應用程序時,我感到非常興奮。 因此,我決定嘗試一下,并分享自己的經驗。
It turns out that once Termux is running on Android and Node is installed, the fact that we are on Android instead of Linux doesn’t make much of a difference. In fact, all the Termux specific setup was done in Part 1 and you are welcome to code along on your preferred device/computer/cloud IDE…
事實證明,一旦Termux在Android上運行并安裝了Node,那么我們使用Android而不是Linux的事實并沒有多大區別。 實際上,所有Termux特定的設置都是在第1部分中完成的,歡迎您在首選的設備/計算機/云IDE上進行編碼……
This also means that, apart from the fact that we substitute Mongo for NeDB, this article is like the usual introduction to building a RESTful API and is mainly for people who are rather new to Node, Express and Mongo/NeDB.
這也意味著,除了我們用Mongo代替NeDB之外,本文就像構建RESTful API的常規介紹一樣,主要針對Node,Express和Mongo / NeDB的新手。
我們要做什么 (What we are going to do)
In order to demonstrate how to get started with the Express web framework and an NeDB database, let’s look at a basic goal tracker that I’ve been building for myself. At the current stage, it looks as shown in the picture above.
為了演示如何開始使用Express Web框架和NeDB數據庫,讓我們看一下我為自己構建的基本目標跟蹤器。 在當前階段,它看起來如上圖所示。
Users can:
用戶可以:
- submit a new goal 提交新目標
- delete a goal 刪除目標
- record a success for a goal 為目標記錄成功
- record a failure for a goal 記錄目標失敗
Well, actually we’re only going to implement the first two features in this post, and in case you’re curious about the two remaining ones, I will provide at the end a link to the code of the full implementation.
好吧,實際上,我們只打算實現本文中的前兩個功能,并且如果您對其余兩個功能感到好奇,我將在最后提供一個指向完整實現代碼的鏈接。
So without the need for recording successes and failures, our code will look a bit simpler:
因此,無需記錄成功和失敗的情況,我們的代碼將看起來更簡單:
Here are the steps we are going to go through:
這是我們要經歷的步驟:
Set up the server with Express.
使用Express設置服務器。
Describe a few user stories.
描述一些用戶故事。
Set up NeDB.
設置NeDB 。
Build a RESTful API.
構建一個RESTful API 。
先決條件 (Pre-requisites)
We are going to start where we left off in Part 1. Thus, the only requirement is that node be installed.
我們將從第1部分中停止的地方開始。因此,唯一的要求就是安裝節點。
1.使用Express設置服務器 (1. Setting up the server with Express)
Express is a web framework for Node that helps build Node applications. If you have trouble figuring out what Express brings to Node, I recommend you check out Evan Hahn’s article Understanding Express.
Express是Node的Web框架,可幫助構建Node應用程序。 如果您無法確定Express給Node帶來了什么,我建議您查看Evan Hahn的文章理解Express 。
Let’s start a new project:
讓我們開始一個新項目:
$ mkdir goals-tracker && cd goals-tracker$ npm init$ touch server.js
and install Express:
并安裝Express:
npm install express --save
We are going to use Express to define the routes, that is to define the application end points (URIs) and set up how the application responds to client requests.
我們將使用Express定義路由,即定義應用程序端點(URI)并設置應用程序如何響應客戶端請求。
Open server.js and copy-paste/write:
打開server.js并復制粘貼/寫入:
With that in place, you can start the app:
有了它,您可以啟動應用程序:
$ node server.js
This should print to the console the number of the port on which the server is listening. If you visit http://localhost:8080 in the browser (assuming that 8080 is the number that got printed to the console) you should see on the page: Our first route is working. :)
這應該在控制臺上顯示服務器正在偵聽的端口號。 如果您在瀏覽器中訪問http:// localhost:8080 (假設8080是打印在控制臺上的數字),則您應該在頁面上看到: 我們的第一個途徑正在起作用。 :)
一些解釋 (Some explanations)
The ‘/’ in app.get( … ) defines the route where we want to attach some behavior from the server. Using ‘/’ refers to the base URI, in our case: http://localhost:8080. Note that we would get the same result in the browser window if we used app.get(‘/goals’, …) instead and visited http://localhost:8080/goals.
app.get(…)中的'/'定義了我們要從服務器附加某些行為的路由。 在本例中,使用'/'表示基本URI: http:// localhost:8080 。 請注意,如果我們改用app.get('/ goals',…)并訪問http:// localhost:8080 / goals ,則會在瀏覽器窗口中獲得相同的結果。
The second argument in app.get( … ) is a callback function that enables us to define what the server should do when the route given as the first argument is visited. In this function:
app.get(…)中的第二個參數是一個回調函數,使我們能夠定義在訪問作為第一個參數給出的路由時服務器應執行的操作。 在此功能中:
req stands for the request: this is the information that the server receives from a client (for example this might come from someone using the front-end part of the website/app).
req代表請求:這是服務器從客戶端收到的信息(例如,該信息可能來自使用網站/應用程序前端部分的人)。
res stands for response: this is the information that the server sends back to the user. This can be a webpage or some other data like an image, some JSON or some XML.
res代表響應:這是服務器發回給用戶的信息。 這可以是網頁或其他一些數據,例如圖像,一些JSON或一些XML。
Nodemon (Nodemon)
In the next parts of this tutorial we are going to make multiple changes to the file server.js. In order to avoid restarting the server manually each time to see the result, we can install nodemon.
在本教程的下一部分中,我們將對文件server.js進行多次更改。 為了避免每次手動重新啟動服務器來查看結果,我們可以安裝nodemon 。
Nodemon is a utility that will monitor for changes in your code and automatically restart the server. We are going to install it as a development only dependency using the tag save-dev:
Nodemon是一個實用程序,它將監視代碼中的更改并自動重新啟動服務器。 我們將使用標簽save-dev將其安裝為僅限開發的依賴項:
npm install nodemon --save-dev
Now you can restart the server with the nodemon command instead of node:
現在,您可以使用nodemon命令而不是node重新啟動服務器:
nodemon server.js
2.用戶故事 (2. The user stories)
Before we move on to the part about NeDB, let’s pause for a moment to think about the business logic. In order to see what we need to implement, we start by defining a few user stories.
在繼續介紹有關NeDB的部分之前,讓我們暫停一下,思考一下業務邏輯。 為了了解我們需要實現的內容,我們首先定義一些用戶故事。
A user story is a very high-level definition of a requirement. User stories are useful for discussing the product in non-technical terms with a client, for estimating how much time and effort the implementation of a feature will take, for guiding the overall development of an application, and for doing Test Driven Development.
用戶故事是需求的非常高級的定義。 用戶故事對于與客戶以非技術性的角度討論產品,估計實現功能所需的時間和精力,指導應用程序的整體開發以及進行測試驅動開發很有用。
Here are the 4 user stories we’re going to use:
這是我們將要使用的4個用戶故事:
- As a user, I can save a new goal with its date of creation. 作為用戶,我可以保存一個新目標及其創建日期。
- As a user, I can access all the goals that have been saved. 作為用戶,我可以訪問所有已保存的目標。
- As a user, I can access the whole information about a goal. 作為用戶,我可以訪問有關目標的全部信息。
- As a user, I can delete a goal. 作為用戶,我可以刪除目標。
In our case, the stories map one-to-one to the 4 CRUD operations that we are going to talk about in Part 4.
在我們的案例中,故事一對一映射到我們將在第4部分中討論的4個CRUD操作。
3.使用NeDB (3. Using NeDB)
The fact that NeDB is easy to install, well documented and uses the MongoDB’s API makes it perfect for getting started with developing Node.js applications on Android. There even is a tool to help you switch to MongoDB later on if need be (I haven’t tried it yet, though).
NeDB易于安裝,文檔齊全并且使用MongoDB的API,這一事實使其非常適合開始在Android上開發Node.js應用程序。 甚至還有一個工具可以幫助您在以后需要時切換到MongoDB(盡管我還沒有嘗試過)。
So let us add NeDB to the project:
因此,讓我們將NeDB添加到項目中:
$ npm install nedb --save
and add to server.js a few lines to setup the database and make sure we can save to it:
并在server.js中添加幾行來設置數據庫,并確保我們可以保存到數據庫:
A Datastore refers to what would be called a collection in Mongo. We could create multiple datastores if we needed several collections. As demonstrated in NeDB’s documentation, each collection would be saved in a separate file. Here we have chosen to store the goals collection in a file named goals.db.
數據存儲區指的是Mongo中的集合。 如果需要幾個集合,我們可以創建多個數據存儲。 如NeDB文檔所示,每個集合將保存在單獨的文件中。 在這里,我們選擇將目標集合存儲在名為Goals.db的文件中。
檢查是否有效 (Checking if it worked)
If the server was started earlier with nodemon, it should have updated after the changes in server.js got saved. This means that db.insert(…) should have run and the goal should have been logged to the console:
如果服務器是使用nodemon較早啟動的 ,則應該在保存server.js中的更改后進行更新。 這意味著db.insert(…)應該已經運行,并且目標應該已經記錄到控制臺:
$ nodemon server.js[nodemon] 1.9.1[nodemon] to restart at any time, enter `rs`[nodemon] watching: *.*[nodemon] starting `node server.js`Listening on port 8080[nodemon] restarting due to changes...[nodemon] starting `node server.js`Listening on port 8080{ description: 'Do 10 minutes meditation every day', successes: [], failures: [], _id: 'ScfixKjsOqz9xBo5', createdAt: Fri Mar 18 2016 22:10:58 GMT+0000 (UTC), updatedAt: Fri Mar 18 2016 22:10:58 GMT+0000 (UTC) }
A new file called goals.db should also have been created.
還應該創建一個名為Goal.db的新文件。
$ ls goals.db node_modules/ package.json server.js
And it should contain the goal that just got saved.
它應該包含剛剛保存的目標。
$ less goals.db{"description":"Do 10 minutes meditation every day","_id":"ScfixKjsOqz9xBo5","createdAt":{"$$date":1458339058282},"updatedAt":{"$$date":1458339058282}}
Note that the fields _id, createdAt and updatedAt have been filled in automatically by NeDB because we set up the Datastore with the option timestampData set to true.
請注意,fields _id,createdAt和updatedAt已經自動NeDB填寫,因為我們建立了數據存儲與timestampData設置選項設置為true。
4.構建RESTful API (4. Building a RESTful API)
Next, let’s build a RESTful API for the application. In a nutshell, this means that we want to use HTTP verbs and URIs in order to allow the client to perform CRUD operations (Create, Read, Update and Delete). These operations are also usually going to send back data to the client.
接下來,讓我們為應用程序構建一個RESTful API。 簡而言之,這意味著我們要使用HTTP動詞和URI,以便允許客戶端執行CRUD操作(創建,讀取,更新和刪除)。 這些操作通常還會將數據發送回客戶端。
In CRUD terms we can :
用CRUD術語,我們可以:
Create data with POST,
使用POST 創建數據,
Read data with GET,
使用GET 讀取數據,
Update data with PUT or PATCH,
使用PUT或PATCH 更新數據,
Delete data with DELETE.
使用DELETE 刪除數據。
The HTTP verbs that we are going to use in this post are POST, GET and DELETE.
我們將在本文中使用的HTTP動詞是POST,GET和DELETE。
我們的API (Our API)
Here is a table showing the routes that we are going to set up, how they will be accessed (i.e. with which HTTP Verb) and what each one makes possible:
下表顯示了我們將要設置的路由,如何訪問它們(即使用哪個HTTP Verb)以及每個路由可能實現的功能:
If you want to learn more about RESTful APIs, you could check out Designing a RESTful Web API by Mathias Hansen or Using HTTP Methods for RESTful Services.
如果您想了解有關RESTful API的更多信息,可以查看Mathias Hansen的Designing RESTful Web API或使用RESTful Services使用HTTP方法 。
測試API (Testing the API)
We are going to test the API manually in the terminal using curl. If you are not on Android and would rather like to use a GUI to test the API, you could use POSTMAN.
我們將使用curl在終端中手動測試API。 如果您不在Android上,并且想使用GUI來測試API,則可以使用POSTMAN 。
Let see a first example of how to use curl. Make sure the server is running, open another terminal (in Termux swipe to the right from the left border and click on new session) and type:
讓我們看一下如何使用curl的第一個例子。 確保服務器正在運行,打開另一個終端(在Termux中,從左邊框向右滑動并單擊new session ),然后鍵入:
$ curl -X GET "http://localhost:8080"
This should print to the console what we got in the browser window earlier on, that is: Our first route is working. :)
這應該將我們先前在瀏覽器窗口中看到的內容打印到控制臺,即: 我們的第一個路線正在工作。 :)
We will now add code to server.js bits by bits. In case you would rather like to see the ‘big’ picture first you can head over to the final server.js file.
現在,我們將代碼逐位添加到server.js中 。 如果您想首先看到“大”的圖片,可以轉到最終的server.js文件 。
人體解析器 (Body-parser)
To handle the requests that the server receives, we are going to install body-parser. It processes the incoming requests and makes it easier for us to access the relevant parts.
為了處理服務器收到的請求,我們將安裝body-parser 。 它處理傳入的請求,使我們更容易訪問相關部分。
npm install body-parser --save
Add the body-parser setup code to the top of server.js (e.g. right after the setup of the port number):
將正文分析器設置代碼添加到server.js的頂部(例如, 緊隨端口號設置之后):
實現所有目標 (Getting all the goals)
If the server receives a GET request at /goals, the callback will be executed and the database will be queried with db.find({}). Here the object passed to find() is empty. This means that no constraint is set to what we are looking for and all objects in the database should be returned.
如果服務器在/ goals處收到GET請求,則將執行回調,并使用db.find({})查詢數據庫。 這里傳遞給find()的對象為空。 這意味著沒有為我們要查找的內容設置任何約束,并且應該返回數據庫中的所有對象。
Notice also that no callback has been specified to find(). Thus a Cursor object is returned, which can first be modified with sort, skip or limit before we use exec(callback) to finish the query. Here we are using sort to return the goals with the most recently updated ones at the top (i.e. the ones with the ‘greater’ date of last update).
還要注意,沒有為find()指定回調。 這樣就返回了一個Cursor對象,在使用exec(callback)完成查詢之前,可以先使用sort , skip或limit對其進行修改。 在這里,我們使用排序來返回目標,這些目標的頂部是最近更新的目標(即,上次更新日期為“較大”的目標)。
If everything went well, the result of the query (in our case an array of goals) is sent back to the client formatted as JSON. In case something went wrong and an error is produced, the error message is sent back to the client instead.
如果一切順利,查詢結果(在本例中為目標數組)將以JSON格式發送回客戶端。 如果出現問題并產生錯誤,則會將錯誤消息發送回客戶端。
Let us test if it works:
讓我們測試一下是否可行:
$ curl -X GET "http://localhost:8080/goals/"
This should print to the console an array containing the goal we saved to the database earlier on.
這應該在控制臺上打印一個數組,其中包含我們之前保存到數據庫中的目標。
建立目標 (Creating a goal)
req.body contains key-value pairs of data that was submitted in the request body. By default, it is undefined and it gets populated by the body-parser middleware. In our case the request should contain a key-value pair whose key is named ‘description’ and whose value is thus retrieved by using req.body.description.
req.body包含在請求正文中提交的鍵-值數據對。 默認情況下,它是未定義的,并且由主體解析器中間件填充。 在我們的示例中,請求應包含一個鍵值對,其鍵名為“ description” ,因此可以使用req.body.description檢索其值。
So first, the goal we want to insert into the database is built from the request using req.body.description. Then it can be inserted into the database and if there was no error the response is sent back to the server as JSON.
因此,首先,我們要插入數據庫的目標是使用req.body.description從請求中構建的 。 然后可以將其插入數據庫,如果沒有錯誤,則將響應作為JSON發送回服務器。
Now let us try to POST a new goal using curl:
現在讓我們嘗試使用curl來發布新目標:
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "description=Read about functional programming every day" "http://localhost:8080/goals/"
This should print the JSON representation of the goal that has been sent back to the client.
這應該打印已發送回客戶端的目標的JSON表示形式。
We post the data as x-www-form-urlencoded. This sends the data as query strings that are parsed by the body-parser.
我們將數據發布為x-www-form-urlencoded 。 這會將數據作為由body-parser解析的查詢字符串發送。
使用其ID獲取目標 (Getting a goal using its id)
req.params is an object containing properties mapped to the route “parameters”. Here it enables us to access the value of the goal’s id, which is supposed to come after /goals/ in the URL in the request. For this to work, we have to use a colon in the URI in front of the property that we want to access with req.params.
req.params是一個對象,其中包含映射到路由“參數”的屬性。 在這里,我們可以訪問目標ID的值,該值應該位于請求中URL中的/ goals /之后。 為此,我們必須在要使用req.params訪問的屬性前面的URI中使用冒號。
Apart from the fact that we are using findOne(…) instead of find(…), nothing new here. So let’s test it. For this, you might check what got printed to the console after we saved a new goal using POST and use its “_id” value. Here is my command with the id I got:
除了我們使用的是findOne(...)而不是find(...)以外 ,這里沒有什么新內容。 因此,讓我們對其進行測試。 為此,您可以檢查在使用POST保存新目標并使用其“ _id”值后打印到控制臺的內容。 這是我的命令,其ID為:
$ curl -X GET "http://localhost:8080/goals/JJtcFVQnoAxW7KXc"
This should print to the console the goal with the given id.
這應該將具有給定ID的目標打印到控制臺。
使用其ID刪除目標 (Deleting a goal using its id)
We use remove(…) to delete a goal from the database. If the deletion is successful, the response is sent with the HTTP status code 200 (200 means that the deletion was successful).
我們使用remove(…)從數據庫中刪除目標。 如果刪除成功,則使用HTTP狀態代碼200發送響應( 200表示刪除成功 )。
結語 (Wrapping it up)
We have set up a server with Express and NeDB, and built a REST API. What we are still missing is a front-end and a bit of wiring.
我們已經使用Express和NeDB設置了服務器,并構建了REST API。 我們仍然缺少的是前端和一些布線。
This next step could take us down many different roads: Would we opt for a template engine and if yes which one? Bootstrap or a similar framework? Angular, React, Aurelia? The list just goes on and on.
下一步可能會帶我們走許多不同的道路:我們會選擇模板引擎嗎? 引導程序還是類似的框架? Angular,React,Aurelia? 這份清單不勝枚舉。
In case you would like to have a look at a minimal implementation of a front-end — and maybe play around with it in your browser — you can see the code of a possible solution I have been implementing using Handlebars, Material Design Lite and the fetch API by visiting its repo on GitHub or by cloning it:
如果您希望了解前端的最小實現,并且可以在瀏覽器中試用它,則可以看到我一直在使用Handlebars , Material Design Lite和通過訪問GitHub上的倉庫或克隆它來獲取API :
$ git clone --branch rest-and-view https://github.com/aurerua/goals-tracker.git --depth 1
更進一步 (Going further)
The back-end that we have built still raises the question: how should the code be split into different files and folders for better modularity and maintainability?
我們構建的后端仍然引發一個問題:如何將代碼拆分為不同的文件和文件夾,以實現更好的模塊化和可維護性?
In case you’re curious, I have also been writing another version of the app that uses a Model-View-Controller folder structure. Feel free to have a look:
萬一您感到好奇,我還一直在編寫使用Model-View-Controller文件夾結構的應用程序的另一個版本 。 隨意看看:
$ git clone https://github.com/aurerua/goals-tracker-mvc.git
And if you have any question or feedback, do not hesitate to contact me!
如果您有任何疑問或反饋,請隨時與我聯系!
翻譯自: https://www.freecodecamp.org/news/building-a-node-js-application-on-android-part-2-express-and-nedb-ced04caea7bb/
構建node.js基礎鏡像