Node.js 学习笔记 - http模块

在Node.js 中有一个内置模块叫http,我们可以用这个模块快速的开发一个http服务。

一、最基础版本

index.js

const http = require('http');

const server = http.createServer();

server.on('request', (request, response) => {
  response.statusCode = 200;
  response.end('hello Node.js');
});

server.listen(3000);
复制代码

在终端启动这个服务 node index.js

通过postman请求 http://localhost:3000,或者直接在浏览器地址栏中输入http://localhost:3000,你就会看到hello Node.js这行字。

二. 不同的url路径返回不同的内容

const http = require('http');

const server = http.createServer();

server.on('request', (request, response) => {
  const { url } = request;
  console.log(url);
  response.statusCode = 200;
  switch (url) {
    case '/hello':
      response.end('Hello, meet again.');
      break;
    case '/bye':
      response.end('Talk to you next time');
      break;
    default:
      response.end("I don't know what you say.");
      break;
  }
});

server.listen(3000);
复制代码

效果:

通过request中的url,就可以根据路径的不同返回不同的内容,注意request.url会匹配到域名后的所有内容。

三. 处理get请求的参数

const http = require('http');
const urlParse = require('url');
const qs = require('querystring');

const server = http.createServer();

server.on('request', (request, response) => {
  const { url } = request;
  const { query, pathname } = urlParse.parse(url);
  const { amount } = qs.parse(query);
  response.statusCode = 200;

  switch (pathname) {
    case '/borrowMoney':
      if (Number(amount) > 500) {
        response.end('go away');
      } else {
        response.end('here you are');
      }
      break;
    case '/bye':
      response.end('Talk to you next time');
      break;
    default:
      response.end("I don't know what you say.");
      break;
  }
});

server.listen(3000);

复制代码

通过url这个模块,可以将请求的url分解成这样一个对象:

Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?amount=100',
  query: 'amount=100',
  pathname: '/borrowMoney',
  path: '/borrowMoney?amount=100',
  href: '/borrowMoney?amount=100'
}
复制代码

然后通过querystring再将分解后的url对象中的query用=分割,这样就可以处理get请求带的参数了。

四. 处理不同的请求方法

HTTP请求有很多方法,常见有:GET,POST,PUT,DELETE,OPTIONS,PATCH等,在restful风格下的接口设计,会是同一个url,通过方法的不同来判断你的这个请求要做什么,比如:

http://localhost:3000/user
复制代码

就只有这样一个接口,用POST请求新建用户,GET请求读取用户,PATCH请求更新用户,DELETE请求删除用户。

RESTful API 最佳实践

要实现RESTful风格的接口,服务器就要进行请求方法的判断。

const http = require('http');
const urlParse = require('url');
const qs = require('querystring');

const server = http.createServer();
let users = [];
let userId = 0;

function updateUsers(res, method, id) {
  switch (method) {
    case 'GET':
      res.end(JSON.stringify(users));
      break;
    case 'POST':
      userId += 1;
      const user = { id: userId };
      users.push(user);
      res.end(JSON.stringify(user));
      break;
    default:
      res.statusCode = 405;
      res.end('Method Not Allowed');
      break;
  }
  users = users.filter(item => item.id !== id);
  res.end(JSON.stringify(users));
}
server.on('request', (request, response) => {
  const { url, method } = request;
  const { pathname, query } = urlParse.parse(url);
  const { id } = qs.parse(query);
  response.statusCode = 200;
  switch (pathname) {
    case '/user':
      updateUsers(response, method, id);
      break;
    default:
      response.end("I don't know what you say.");
      break;
  }
});

server.listen(3000);

复制代码

通过request获取到请求的方法,然后再去根据方法做相应的处理。

五. 处理请求体中的数据

上面的例子中请求的数据都是通过url的query这种方式发送的,但是实际情况下像post请求一般会把请求数据放在请求体中,接下来就看看如何处理请求体中的数据。

看看postman上的body一栏:

可以看到请求体中的数据类型还是很多的,这些类型通过请求头中的Content-Type来决定。

const http = require('http');
const urlParse = require('url');

const server = http.createServer();
const users = [];
server.on('request', (request, response) => {
  const { url, method, headers } = request;
  const { pathname } = urlParse.parse(url);
  const contentType = headers['content-type'];
  response.statusCode = 200;
  switch (pathname) {
    case '/user':
      switch (method) {
        case 'GET':
          response.end(JSON.stringify(users));
          break;
        case 'POST':
          if (contentType !== 'application/json') {
            response.statusCode = 400;
            response.end('error');
            break;
          }
          let requestBodyStr = '';
          request.on('data', (data) => {
            requestBodyStr += data.toString();
          });
          request.on('end', () => {
            const user = JSON.parse(requestBodyStr);
            users.push(user);
            response.end(JSON.stringify(user));
          });
          break;
        default:
          response.statusCode = 405;
          response.end('Method Not Allowed');
          break;
      }
      break;
    default:
      response.end("I don't know what you say.");
      break;
  }
});

server.listen(3000);

复制代码

Content-Type 可以从request的headers中获取。

比较关键的就是这里:

let requestBodyStr = '';
request.on('data', (data) => {
    requestBodyStr += data.toString();
});
request.on('end', () => {
    const user = JSON.parse(requestBodyStr);
    users.push(user);
    response.end(JSON.stringify(user));
});
复制代码

要处理请求体中的数据,得让request去监听data和end事件。

这样做的原因是,有可能请求中的数据量很大,比上传一个文件,如果你一次性将数据放在存进内存,那么成百上千个请求发送过来内存就爆了。为了解决这个问题node.js使用了一种叫“流”的数据结构来处理这种情况。打个比方:现在有一池塘水,一次性挑完是不现实的,只有一桶一桶的去挑,当你挑的次数足够多的时候那么池塘的水就会被挑完。

data事件表示流的数据已经可以读取了

end事件表示这个流已经到末尾了,没有数据可以读取了。

除了这两个之外还有一个error事件,表示出错。

如果一桶挑不完,data事件会被触发多次的。end事件只会触发一次

做个试验:

const http = require('http');
const urlParse = require('url');

const server = http.createServer();
server.on('request', (request, response) => {
  const { url } = request;
  const { pathname } = urlParse.parse(url);
  response.statusCode = 200;
  switch (pathname) {
    case '/upload':
      let count = 0;
      request.on('data', () => {
        count += 1;
      });
      request.on('end', () => {
        response.end(`${count}`);
      });
      break;
    default:
      response.end("I don't know what you say.");
      break;
  }
});

server.listen(3000);

复制代码

通过form-data格式传一个rar格式的文件。这个文件大小是65.2MB

结果

node.js 用了 1354 次将池塘里的水挑完。

当都实现了上面的功能后,一个简易的http服务就写好了。

转载于:https://juejin.im/post/5cf537f96fb9a07efb6970de

猜你喜欢

转载自blog.csdn.net/weixin_33735077/article/details/91462820