express遇到的問題


1. 如何引入express?

 cnpm install express --save 

其中--save可以保存到依賴項中。

接着 var express = require("express"); 即可。這里express只是一個模塊。

 注意: 有時候我們會看到有人使用  npm i express --save 的方式來安裝,這樣也是可行的,因為 npm i 就是 npm install 的簡寫形式。 

 

2. 什么是並且如何使用express-generator?

這是一個生成express的生成器,通過它,我們可以快速構建一個express架構,而無需自己繁瑣的一項一項構建。

  cnpn install express-generator -g (管理員方式打開命令窗口)

注意:如果是在當前目錄安裝就不需要使用管理員方式,但是如果全局安裝,就一定要使用管理員方式。因為這里創建了一個生成器,所以就像構造函數一樣可以去創建實例,那么express在命令行中就是相當於一個可執行文件, 如果不是全局安裝,就必須要在express-generator的文件目錄下才能執行 ,非常繁瑣,但是如果全局安裝,我們在任何目錄下都可以執行該命令。 如:

  express myapp

就可以創建一個express架構。

這里默認使用的模板引擎是jade,如果希望使用ejs模板引擎,可以是 npm -e myapp, 其中的-e就代表使用ejs模板引擎。

 

如下:

注意:即通過express-generator我們再執行 express <項目名稱> 可以快速構建一個架構。 其中myapp就是這樣一個文件,包含了package.json(此文件中的依賴項中包含了各種express所需的包),app.js(即入口文件)、pulic即其下面的一些文件夾用於存放相應的文件 ,以及路由等等。 如果不使用 express-generator ,我們就得自己一個一個的創建,這是相當麻煩的 。 另外,如果有不符合我們項目的地方,我們直接修改即可。 創建的同時提示首先 cd myapp (即進入myapp文件夾)然后 npm install(安裝package.json中的依賴項),完成依賴項的安裝(即package.json文件中的依賴項安裝)。我們可以看到package.json中的依賴項如下:

{
  "name": "myapp",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "express": "~4.15.2",
    "jade": "~1.11.0",
    "morgan": "~1.8.1",
    "serve-favicon": "~2.4.2"
  }
}

其中body-parser在post請求時必須使用;cookie-parser在處理cookie時必須使用; debug模塊用於調試,類似於console.log; express就更不用說了;jade是模板引擎模塊,這對於服務器端語言還是非常重要的。morgan是一個日志模塊,用於在后台打印出req請求等等,方便我們在后台查看,這與debug模塊非常相似,但是debug模塊是用於取代console.log的,而morgan主要是用於查看請求的。serve-favicon不太懂,后面學習。

cd myapp
npm install

 注意: 在安裝過程中,會提示jade已經更名為pug,也就是說兩者是一回事。

 

在安裝完了所有的依賴項之后,我們就可以使用下面的命令來啟動這個應用了:

set DEBUG=myapp:* & npm start

這里 set DEBUG=myapp:* & npm start 就可以來啟動了,而前者是說啟動debug模塊,打印一些debug日志方便我們管理后台。  注意:和*之間有空格和沒有空格是不同的。 這里使用的沒有空格。 

 

另外,如果不希望使用debug模塊,像下面這樣就可以啟動了。

npm start

 

 

在瀏覽器中進入localhost:3000, 如下所示:

 

通過 Express 應用生成器創建的應用一般都有如下目錄結構:

 

(補充):其中routes文件是怎么是使用的呢? 

 其實不用routes文件當然也是可以的,但是在實際開發中, 路由文件動輒成百上千,如果全部放在 app.js 中, 不難想象app.js將會多么臃腫, 所以我們需要對於不同路徑的路由放在不同的文件下。

 對於 express-generator 生成的模板, app.js 的內容如下:

var express = require('express');
var app = express();
var indexRouter = require('./routes/index');
var userRouter = require('./routes/users');

app.use('/', indexRouter);
app.use('/users', userRouter);

app.listen(3000);

 然后進入routes下的index我們可以看到:

var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  res.send('hello, express');
});

module.exports = router;

 其中router為express.Router() 的一個實例,這是非常重要的。 

 

 對於routes下的users也是一樣的:

var express = require('express');
var router = express.Router();

router.get('/:name', function(req, res) {
  res.send('hello, ' + req.params.name);
});

module.exports = router;

 這樣就可以很好的管理路由了,當然,不難看出,實際項目中,我們並不是真的只要這兩個路由,而是根據你的項目,可能除了 index、 users, 還有其他的文件,只要保證一個路徑對應一個路由文件即可,這樣便可很好地管理路由了。

 

 重要的是需要在 app.js 中使用 app.use() 掛載到不同的路徑上。

 易錯點: 在使用 app.use() 時,第一個參數是一個相對的路徑,然后使用第二個參數,即routes下的文件時, router.get("/"),這又是一個路徑,最終表現在url上是兩者的綜合路徑(疊加路徑),比如app.use('/reg', index); 其中在index下的路由文件中設置的是 router.get('/reg', function (req, res) {}) , 那么最終表現出來的就是localhost:8888/reg/reg 這樣的路由,這樣才能正確訪問, 否則就會出錯。

 

 

(補)debug模塊的使用。(參考教程

 在上面的例子中,我們使用 set DEBUG=myapp:* & npm start, 其中用到了debug模塊, 實際上debug模塊是怎么使用的呢? 

 nodejs的調試有很多,這里主要介紹debug模塊調試,首先npm init 、npm install debug --save, 新建app.js文件,其內容如下:

var debug = require("debug")("mydebug:http"),
    work = require("./work"),
    http = require("http");
http.createServer(function (req, res) {
    debug(req.method + " " + req.url);
    res.end("hello \n");
}).listen(3000, function () {
    debug("listening");
});

 然后建立work.js,內容如下:

var debug = require("debug")("mydebug:work");
setInterval(function () {
    debug("doing some work @ %s - %s", new Date().toString(), "with supervisor");
}, 2000);

 

(注意:如果要debug, 就必須要在當前目錄下存在 npm-debug.log 日志文件)

  可以看到,這兩個模塊中我都使用了 debug 模塊。 運行 set DEBUG=mydebug:* & node app.js ,如下:

即這里的debug語句就相當於console.log(),然后對於不同的debug,會顯示不同的顯色, 而mydebug是我設置的debug名稱,也可以是其他的。

開啟時使用的是 set DEBUG=mydebug:* & node app , 也就是說我們運行了所有的(*)debug模塊,如果我們只想運行work模塊,而不運行http模塊,可以像下面這樣:

set DEBUG=mydebug:work & node app 

 

我們可以將&理解為並且的意思。(為什么不是&&呢?)

 

 

(補)npm start的使用原理

  這里實際上是 npm run start 的簡寫。 參考 http://javascript.ruanyifeng.com/nodejs/packagejson.html

  這里的npm start啟動的是bin目錄下的www,也就是說使用 express-generator 的默認的入口是 bin 下的 www, 而不是app.js ,目前很多項目都是如此, 我們可以在 package.json中進行設置,如 express-generator 中的設置如下:

{
  "name": "myapp",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "express": "~4.15.2",
    "jade": "~1.11.0",
    "morgan": "~1.8.1",
    "serve-favicon": "~2.4.2"
  }
}

其中的 scripts 中設置了start(入口文件)為 node ./bin/www ,即當啟動項目時,實際上輸入的是 node ./bin/www ,只是這樣設置的好處在於更加方便管理。容易理解。

比如我們創建一個文件,npm init , 創建 app.js,內容如下:

var http = require("http");
http.createServer(function (req, res) {
    res.writeHead(200, {"Content-Type": "text/plain; charset=utf8"});
    res.write("hello");
    res.end();
}).listen(8888, function () {
    console.log("Server is running at port 127.0.0.1:8888");
});
View Code

 

然后node app即可啟動這個項目,但是app就在這當然比較好啟動,可如果入口文件藏的很深呢? 比如./bin/www就深了一層,每次 node ./bin/www可能會比較麻煩,所以在 package.json 中的scripts下添加了 start 選項,對於這個項目我們也可以添加,如下:

{
  "name": "starttest",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js"
  },
  "author": "",
  "license": "ISC"
}

 

即添加start項,每次npm start 就相當於 node app.js,這樣的效果是一樣的。   

我們可以使用 npm run start 和 npm run test 都是可以執行的。 參考阮一峰教程

 

 

(補:)package.json中的dependencies是什么? 作用如何? 和devDependencies的區別是什么?

  即項目依賴項,用於告訴我們這個項目依賴了哪些模塊,但這不是主要作用, 主要作用是當我們將項目上傳到服務器時,可以不用上傳 node_modules ,而只用上傳基礎文件即可, 服務器端可以直接在根目錄下 npm install ,然后就可以安裝package.json中所有的依賴模塊了。

  dev,顧名思義,是開發的意思, 而dependencies是指生產中所需要的依賴項, devDependencies 可能包括一些生產中不需要而僅在開發中需要的調試模塊。

 

 

 

 

 

 

 3. 路由見了很多,到底什么是路由? 句柄又是什么? 路由中有哪些常用的響應方法?

 簡單的理解,路由就是 路徑 + 一個http方法 + 一些句柄。 下面就是一個路由文件:

// 對網站首頁的訪問返回 "Hello World!" 字樣
app.get('/', function (req, res) {
  res.send('Hello World!');
});

// 網站首頁接受 POST 請求
app.post('/', function (req, res) {
  res.send('Got a POST request');
});

// /user 節點接受 PUT 請求
app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user');
});

// /user 節點接受 DELETE 請求
app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user');

  其中app是一個express實例, 同時包含了路徑(/ 、user)和http方法(get、post、delete、put)。  

  有時候我們還會見到app.all() ,這是指不論是什么方法,只要路徑對了,就會執行后面的句柄。

  那么什么是句柄呢?  其實句柄就是指其中的語句, 執行的函數。。。

 

  而路由中一定是有響應方法的,比如之前一直使用的 res.send() 這樣可以把其中的內容返回給頁面,另外,還有下面的一些方法:

其中 res.end() 也比較常用,表示發送結束了。 res.download() 方法接受一個參數是文件的相對路由, 一旦滿足路由,就會下載文件。 res.json() 即接受一個json字符串。如下:

var express = require("express");
var fs = require("fs");
var app = express();
app.get("/", function (req, res) {
    res.json('{"name": "John Zhu"}');
    res.end();
});
app.get("/login", function (req, res) {
    res.download("./test.txt");
});
app.listen(3000, function () {
    console.log("Server is running at localhost:3000");
});

 

另外,對於相同的路徑,根據不同的請求方法給出不同的句柄,我們應該怎么實現呢? 可以是下面這樣:

app.get("/", function (req, res) {
    res.send("get");
});
app.post("/", function (req, res) {
    res.send("post");
});
app.delete("/", function (req, res) {
    res.send("delete");
});
app.put("/", function (req, res) {
    res.send("put");
});

但是這樣顯然代碼是冗余的,並且容易造成拼寫錯誤,如果使用 app.route() 使用鏈式定義會更好,如下:

app.route("/")
.get(function (req, res) {
    res.send("get");
})
.post(function (req, res) {
    res.send("post");
})
.delete(function (req, res) {
    res.send("delete");
})
.put(function (req, res) {
    res.send("put");
});

這樣更容易查看並且不容易出錯。

 

 

 

  

  

 

 

4. express中靜態文件是什么?

 錯!  靜態文件是node中的概念,而不僅僅是express中的。 它的作用就是托管靜態文件。 什么是靜態文件呢?  比如我們看到一個網站上(如網易)的一個圖片,然后復制圖片地址,如http://img3.cache.netease.com/photo/0001/2017-04-21/CII56AJH19BR0001.jpg ,打開這個連接,發現這就是一個圖片, 而這,就是靜態文件。 如我們再引入圖片、css、js等時,這些文件都是靜態文件,由此可知,靜態文件的重要性。

 在使用express-generator生成應用的時候,我們就可以在那個架構中看到 public 文件,這個文件就是靜態文件,其中包含了images、javascripts、stylesheets。 使用如下:

app.use(express.static("public"));

 重要聲明: 其中app是一個express實例,而use就代表使用一個中間件。即使用express.static()中間件。其中一定是express而不是app,這是需要格外注意的地方。 

 

 

5. 剛剛也提到了中間件這個概念,那么到底什么是中間件? 怎么理解中間件中的next()方法?

 在知乎上有這么一個回答,就照搬過來吧~ 

 

  毫無疑問,這里的中間件的定義是便於我們理解的,我們看看官網上是怎么說的吧~

Express 是一個自身功能極簡,完全是由路由和中間件構成一個的 web 開發框架:從本質上來說,一個 Express 應用就是在調用各種中間件

中間件(Middleware) 是一個函數,它可以訪問請求對象(request object (req)), 響應對象(response object (res)), 和 web 應用中處於請求-響應循環流程中的中間件,一般被命名為 next 的變量。

  在express中可以使用下面的幾種中間件:

  應用級中間件:即我們使用的app.use() 和 app.get()之類的中間件。 通過這個我們不難理解,express的確完全是使用中間件搭建起來的。 因為app.METHOD()在服務器端語言node中使用的很多。 

  路由級中間件:即app.Router()的中間件

  錯誤處理中間件:即函數的參數必須要四個,分別是 error、req、res和next。 error就是用來處理錯誤的。

  內置中間件:express中唯一內置的中間件就是 express.static()了。

  第三方中間件:如cookie-parser 這樣的中間件就是第三方中間件。 還有body-parser也是的。

 

 

6. 如何在Express中使用模板引擎?

 說明:模板引擎有很多,比如 jade (下面主要說的)、 ejs (也是非常常用的)等等很多。 

 

  兩個步驟就可以讓express來渲染模板文件:

  第一: 添加放置模板的目錄views, 然后app.set('views', './views');

  第二: 添加模板引擎即views engine, 然后app.set('view engine', 'jade');

  當然前提條件是有相應的模板引擎安裝包:

npm install jade --save

  之前我們使用 express-generator 的時候就可以發現目錄下已經有了 views 目錄, 同時 package.json 中也是有依賴項jade 的, 利用之我們可以看到在app.js中它是這樣設置的。

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

  當然這是一樣的, set函數接受兩個參數,第一個是要設置的東西,這里是模板文件(views), 第二個參數是一個路徑, 不難理解,path.join() 就是為了將兩個path組合到一起  。 當然,前提是引入path模塊。

  對於第二句使用jade模板引擎都是一樣的。

  然后我們再 views 下面生成以及模板文件, 如generator生成的文件就是 index.jade、layout.jade、 error.jade 這三個模板引擎。其中layout.jade如下所示:

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    block content

  這個模板引擎的特有語法, 更加簡潔。 在body下有一個block content, 這時就要根據不同的狀態使用 index.jade 和 error.jade了, 如index.jade內容如下:

extends layout

block content
  h1= title
  p Welcome to #{title} #{title}

  很容易看出來它是對 layout.jade 的擴展, 其定義了block content的內容, 其中的title就是可以替換的變量,后面會講到。而error.jade的內容如下:

extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}

  同樣,這也是對於layout.jade 的擴展。 

  

  在哪里控制這個渲染的呢? 顯然這些都與路由有關,所以在routes下的index.js中可以看到:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

  即使用了res.render()渲染index這個模板引擎(在擴展layout的基礎上),並給這個模板引擎傳遞參數title: 'Express', 這是以對象的形式傳遞的所以說我們還可以傳遞更多的參數。

  更一般的理解: 一旦get方法訪問了主頁,就會將index.jade模板引擎渲染為html頁面返回給用戶。

 

 


 

 開頭就說了,模板引擎有很多,ejs 就是其中一種, 因為它在使用起來非常簡單, 並且與 express 集成良好, 所以我們選用 ejs . (ejs官方文檔

在 express-generator 中自動生成的是jade, 所以如果不實用jade而是使用 ejs 的話,我們就要單獨安裝了,如下所示:  

npm install ejs --save

 

 然后,我們需要在 app.js 中添加下面的代碼:

app.set('views', path.join(__dirname, 'views'));
app.set('views engine', 'ejs');
app.set('view engine', 'ejs');

 也就是說使用views作為放置模板的目錄。 使用ejs作為模板引擎。

 注意: 設置路徑時是 views ,因為模板不止一個,但是在設置模板引擎時,一定是 view engine ,而不能加s, 否則就會報錯。

 另外,在app.js中不需要 require("ejs") ,如果require()了也不會報錯。 

 

 接下來開始設置模板, 在 views 下面添加 user.ejs,其中內容如下:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
    </style>
  </head>
  <body>
    <h1><%= name.toUpperCase() %></h1>
    <p>hello, <%= name %></p>
  </body>
</html>

 這就是ejs的格式, 使用 <% 變量 %>將變量包裹起來。可以看到,在模板中我使用了 name.toUpperCase() 這樣的js語句。

 

 修改 routes下的 user.js 內容如下:

var express = require('express');
var router = express.Router();

router.get('/:name', function(req, res) {
  res.render('users', {
    name: req.params.name
  });
});

module.exports = router;

 這樣就可以成功渲染一個html頁面了。即通過 res.render() 來渲染 ejs 模板, res.render() 的第一個參數是模板的名字,這里的user會匹配 views/user.ejs , 第二個參數是傳給模板的數據,這里傳入了name,那么在模板中就可以使用 name 這個變量了, 所以res.render() 的作用就是將模板和數據結合成HTML,同時在響應頭中設置 {"Content-Type: text/html"} 告訴瀏覽器我渲染的是一個html頁面,而不是文本。

 

 補充說明: ejs有下面幾種常用的標簽:

  • <% code %> 運行js代碼,不輸出
  • <%= code %> 顯示轉義后的html內容
  • <%- code %> 顯示原始html內容

 下面的例子解釋了 <% code %>的用法:

 DATA:

supplies: ['mop', 'broom', 'duster']

 

 EJS TEMPLATE:

<ul>
<% for(var i=0; i<supplies.length; i++) {%>
   <li><%= supplies[i] %></li>
<% } %>
</ul>

 

 RESULT:

<ul>
  <li>mop</li>
  <li>broom</li>
  <li>duster</li>
</ul>

 

 

ejs --- include 


 

我們使用模板通常並不是一個頁面對應一個模板,這樣模板的優勢就失去了。而是把模板拆成可以復用的模板片段組合使用 (這正是我想要的)

比如我在views下新建了 header.ejs 和 footer.ejs,  並修改了 user.ejs,如下所示:
views/header.ejs

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
    </style>
  </head>
  <body>

 

views/footer.ejs

  </body>
</html>

 

views/user.ejs

<%- include('header') %>
  <h1><%= name.toUpperCase() %></h1>
  <p>hello, <%= name %></p>
<%- include('footer') %>

 

 即我們通過 incluede 方式引入了footer和header,這樣,如果文件多了,利用率會更高一些。 注意: 其中 <%- ... %> 表示使用原始數據,即原來是啥就是啥。

 

說明: 拆分模板組件的兩個好處

  • 模板可以復用,減少重復代碼
  • 主模板更加清晰。

 

 

 

 

 

7.怎么理解錯誤處理?  如何進行錯誤處理?

  錯誤處理? 即請求發生錯誤,或者是響應錯誤時,給出一定的處理方案: 如給用戶提示錯誤信息等等。。。

  錯誤處理也是一種中間件,之前我們就說過,express框架就是使用一大推中間件堆積起來的,錯誤處理中間件是一個函數, 必須有四個參數,分別是 err req res next, 值得注意的是,我們必須要在其他中間件定義完了之后,然后在定義錯誤處理中間件。 如下:

var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
  // 業務邏輯
});

  我們可以自己測驗一下,如下所示:

var express = require("express");
var app = express();
app.get('/', function (req, res) {
    // 這個函數沒有定義會發生錯誤。
    god();
});
app.get('/login', function (req, res) {
    res.send("登錄成功!");
});
app.use(function (err, req, res, next) {
    res.send("錯誤發生:" + err);
});
app.listen(3000, function () {
    console.log("server is running at localhost:3000...");
});

  在這里, 我們在‘/’定義了一個 get 請求,然后執行一個沒有定義的函數,那么這一定會出錯,然后不過不使用 app.use(function (err, req, res, next) {...}) 那么錯誤界面就會很亂甚至導致后台崩潰,但是如果我們使用了異常處理中間件, 就會發現, 可以通過它捕獲到請求。 值得注意的是: 異常處理中間件一定要放在最后(listen之前), 這樣就可以成功的異常處理了。

  如下:

錯誤發生:ReferenceError: god is not defined

  當然我們也可以打印出 錯誤棧(error stack), 即

res.send("錯誤發生:" + err.stack);

  效果如下:

 

 

 8. nodejs作為后台語言,怎么集成數據庫

 的確,后台語言大半時間也是為了和后台打交道的,所以數據庫的連接和使用格外重要。 要為nodejs連接數據庫,只需要添加相應的驅動即可。下面是一些常用的數據庫node模塊。

 

 這里主要講MySQL 和 MongoDB,如下所示:

MySQL --- 首先安裝mysql模塊:

npm install mysql

 

然后連接數據庫:

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'dbuser',
  password : 's3kreee7'
});

connection.connect();

connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;
  console.log('The solution is: ', rows[0].solution);
});

connection.end();

 

 

 

MongoDB --- 首先安裝mongoskin模塊:

npm install mongoskin 

 然后連接數據庫:

var db = require('mongoskin').db('localhost:27017/animals');

db.collection('mamals').find().toArray(function(err, result) {
  if (err) throw err;
  console.log(result);
});

 

 

 

8. app.get("/logim/:name", function () {}); 是什么意思? 那里的:的作用是什么?

  其中:name是占位符的意思,如:

app.get("/login/:name", function (req, res) {
    res.send(req.params.name);
});

  如果我們輸入 localhost:8888/login/hhh ,那么瀏覽器上就會顯示hhh,因為我們可以通過 req.params.name 讀取到這個占位符。

 

 

 

 

 

推薦github項目: https://github.com/nswbmw/N-blog/blob/master/book/3.3%20%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E.md

結束


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM