zoukankan      html  css  js  c++  java
  • 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

    结束

  • 相关阅读:
    [转]MySQL之外键约束
    [转]mysql忘记密码,如何重置密码
    [转]MS Sql 7105错误
    [轉]免费的.NET混淆和反编译工具
    一個PDO操作類
    [转]基于FPGA的八位RISC CPU的设计
    SQL Server 2005中的分区表
    [轉]使用SQLDMO备份和恢复数据库(VB & ASP)
    [轉]VS 2010 通过 Architecture创建UML类图设计
    [转]用CSS调整scrollbar(滚动条)的配色
  • 原文地址:https://www.cnblogs.com/zhuzhenwei918/p/6749502.html
Copyright © 2011-2022 走看看