node.js+express搭建简单的web服务器

前言

  有朋友问我关于nodejs搭建web服务器的问题,我对node的了解也不多,所以简单的学习了解一下,帮朋友解决了一些问题。

  首先,很多人都在问nodejs适不适合web服务器开发。这个问题知乎上面已经有很多解答了,比较详细点赞较多的一条:使用 Node.js 的优势和劣势都有哪些? - FengqiAsia的回答 - 知乎,年代久远不知道是否适用。看了相关的介绍之后,我觉得nodejs是非常适合做web服务器的,网上大多的评价都是可靠性低,单进程、单线程,一个崩掉整个进程就崩掉之类的,异常难以定位。还有可能是从传统语言转过来写js的话,可能在一段时间内很难适应吧。

  看了下,linkedin、yahoo的服务器就是用node写的,还有之前去了解的unity-cache-server,说明node的优点是毋庸置疑的。可靠性低进程崩掉等缺点其实是都可以通过代码的健壮性来避免的(说着轻巧),另外异常处理相关的也有很多库和进程管理程序监控node的运行。

作者:尤雨溪

链接:https://www.zhihu.com/question/20069184/answer/14385915

来源:知乎

异步的思维是js的特点,也是node高并发性能优势的原因之一,你从传统的同步语言过来可能不习惯,但是像我们这种从前端写js过来的人就自然得像说话一样,关键还是适应。熟悉之后可以用async,Promise系 (q, bluebird) 或者 eventproxy 之类的库来改善代码嵌套的问题。

异常的问题 - Node 核心库的 API 抛异常大致有三种常见情况:
\1. 异步回调。按惯例,接收的回调函数第一个参数都是可能出现的异常,没有特殊情况的话你应该把异常按照同样的参数位置一层层传下去,直到最顶层的回调里进行统一处理。
\2. 同步版本的api会直接抛异常。所以如果确实无法避免抛错的可能,直接 try catch,要么就避免用同步版本。
\3. Stream形态的API,必须在stream对象上添加 error 的侦听函数,不然异常会直接抛出。

如果出现导致进程中断的异常,说明你的代码有逻辑层面的问题(以上几点没有完全做好),你应该在开发的时候发现并处理这些异常,而不是让它们在部署环境中发生。

如果你实在避免不了问题发生,你可以用 Node 的 Domain API 来对整块代码的异常进行捕捉。
另外可以用进程管理工具比如 forever, pm2 或是 monit 监视应用进程,崩溃后自动重启。

最后回到你的问题,node是否适合做web开发 - node的独特优势是高并发,高实时性,或者单页富前端的web应用,比如实时聊天,游戏,另外node也是写JSON API的最好选择。

  另外看到了一条关于node的应用场景的回答,比较赞同,找不到链接了。大概是说node的适合 io 密集型的应用,能发挥出很好的性能,而 cpu 密集型的应用可能性能就不是最佳选择。估计这就是unity官方选择nodejs来写cacheserver的原因吧。

一个简单的web服务器

  这里用了nodejs的第三方库express,express也有自己的脚手架。新建一个文件夹http-server,cd到该目录下:

touch app.js # 新建app.js文件
# 然后一顿回车,使用默认就ok,不过author可以填上自己的名字。
package name: (http-server)
version: (1.0.0)
description:
entry point: (app.js)
test command:
git repository:
keywords:
author: wangx
license: (ISC)
About to write to /Users/wangx/work/nodejs/http-server/package.json:

{
  "name": "http-server",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "wangx",
  "license": "ISC"
}
# 初始化
npm init
# 安装express
npm install express --save

  然后就可以在app.js写相关逻辑:

/**
 * http服务器
 */
// 引入express
const express = require('express');

//  创建express服务器
const app = express();

// get请求
app.get('/', function (request, response) {
    // console.log(request)
    response.send('get请求成功')
});

// post请求
app.post('/', function (request, response) {
    response.send('post请求成功')
});

// 绑定监听端口
app.listen(9001);
console.log('server启动成功')

  try it,浏览器中输入localhost:9001就可以看到get请求成功了。简单的http服务是这样了。如果简单的写一个页面测试的话会有跨域问题:

app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*"); 
    res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

Access-Control-Allow-Origin:允许的域,*为任意的域都可以访问

Access-Control-Allow-Headers:允许的header类型

Access-Control-Allow-Methods:允许的请求方法

  路由:针对不同的路由有不同的处理方式

app.get('/home', function (request, response, next) {
    console.log('获取数据');
    next();
}, function (request, response) {
    response.send('get请求成功')
});

  get请求参数:request.query会把请求参数包装成字典对象,可通过点运算符获取请求参数

app.get('/home', function (request, response, next) {
    console.log(request.query.name);
    next();
}, function (request, response) {
    response.send(`${request.query.name}请求成功`);
});

localhost:9001/home?name=wangx // wangx  wangx请求成功

  中间件:发送一个请求给服务器的时候,会被中间件拦截,先由中间件处理,每个中间件都有一个回调函数作为参数,拦截到参数,就会自动执行回调函数。有中间件,会先执行中间件的回调函数,然后才会调用get或者post的回调函数,也就是当监听到请求,先执行中间件,才会到get、post请求。

// 中间件
// 截取请求、拦截回调
server.use('/home', function (request, response, next) {
    console.log('中间件')
    console.log(request.query.name)
    next()
});

  post请求参数:nodejs需要使用body-parse解析post请求参数,采用中间件解析post请求参数。完整代码:

// 引入express
const express = require('express');
// 引入 body-parse
const bodyParse = require('body-parse');
//  创建express服务器
const app = express();
// 解析器
const urlencoded = bodyParse.urlencoded({ extends:true });
// 中间件
const jsonParse = bodyParse.json();

// 通过中间件处理,解析请求参数,存放在request.body中
server.use('./home', jsonParse);

// 处理请求参数
app.post('./home', function (request, response) {
    console.log(request.body);
    response.send(request.body);
});

// 解决跨域
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

// 绑定监听端口
app.listen(9001);
console.log('server启动成功')

  仅作为一个demo,关于node服务器的开发与设计,应该参考一下github上成熟的nodejs项目。

  基于nodejs+mongodb构建的饿了么后台系统,这是一个比较好参考的项目,前后端分离,内容比较完整,可以作为学习参考。