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 
");
}).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

结束

原文地址:https://www.cnblogs.com/zhuzhenwei918/p/6749502.html