feng xiaohan

核心模块

http

帮助我们启动服务器或执行发送请求等其他任务。

const http = require("http");

导入模块,它会自动在全局模块(这是在 node.js 中自带的模块)中寻找。

createServer()

创建服务器并返回 server 对象。接收一个函数作为参数,每当有请求时就会执行这个函数。函数有两个默认参数——请求的信息,和响应的信息:

const server = http.createServer((req, res) => {
  // req:包含请求的详细信息
  console.log(req.url); // 发起请求的地址(http://localhost:3000/后的地址)
  console.log(req.method); // 请求方法(GET/POST等)
  console.log(req.headers); // 请求头信息

  // res:服务端响应回去的对象
  res.setHeader("Content-Type", "text/html"); // 设置响应头(响应的文本为HTML)
  // 写入文本信息(此处为HTML)
  res.write("<html>");
  res.write("<head><title>First page</title></head>");
  res.write("<body><h1>Hello,Node.js</h1></body>");
  res.write("</html>");

  res.end(); // 结束编写,发送响应给浏览器(此后的写入无效)
});
server.listen(3000);

注意:res.write()也可以使用模板字符串直接写入。

我们可以通过这个返回的 server 对象来对 server 进行操作:

  • **listen()**:确保 node.js 不会立刻退出我们的代码,而是通过listen()保留它的进程。listen()有几个参数:

    第一个参数:需要监听的端口号;

    一般在生产环境中不需要填写,默认端口为 80;如果本地开发则可能需要不同的端口。

    server.listen(3000);
    

    现在启动 node.js 执行文件后,node.js 可以监听到访问本地 3000 地址后的请求,然后执行createServer()的里的函数。

https

帮助我们启动一个 SSL 加密的服务器。

fs

帮助我们操作文件,对文件进行读写操作。

const fs = require("fs");

writeFile()

写入一个文件,接收三个参数:

  • 第一个参数:完整文件名
  • 第二个参数:文件内容
  • 第三个参数:回调函数,接收一个 error 对象,如果执行出现错误可以在这里得到
fs.writeFile("message.txt", "ds");

writeFileSync()

同步写入文件,在写入文件完成后才能执行下一步。用法与writeFile()相同。

readFile()

读取指定路径的文件,接收两个参数:

  • 第一个参数:文件路径
  • 第二个参数:回调函数,接收一个读取错误时的对象(error 对象)和文件内容
fs.readFile(path, (err, fileContent) => {});

path

帮助我们构建文件路径,让文件的路径能够在任何操作系统(Windows/Mac/Linux)上运行。

const path = require("path");

join()

使用特定于平台的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径。

path.join("/foo", "bar", "baz/asdf", "quux");
// 返回:\foo\bar\baz\asdf\quux

path.join(__dirname, "..", "views", "shop.html");
// __dirname: 当前文件夹的父目录
// '../': 上级目录
  • 返回路径在不同的环境下可能有所不同;
  • 如果有任何路径片段不是字符串,则抛出 TypeError。

dirname()

返回 path 的目录名,类似于 Unix dirname 命令。 尾随的目录分隔符被忽略。

path.dirname("/foo/bar/baz/asdf/quux");
// 返回: '/foo/bar/baz/asdf'

如果有任何路径片段不是字符串,则抛出 TypeError。

  • 使用辅助函数来构建路径

    util/path.js

    const path = require("path");
    
    // process.mainModule(require.main).filename 为我们提供了文件路径,该文件负责我们应用程序的运行,而这个文件名就是通过我们放在dirname里文件路径获取到的
    module.exports = path.dirname(require.main.filename);
    
    const rootDir = require("../util/path");
    
    res.sendFile(path.join(rootDir, "views", "shop.html"));
    

    这样我们就不需要编写根目录了。

os

帮助我们了解操作系统相关信息。

crypto

帮助我们将数据进行加密。

const crypto = require("crypto");

randomBytes()

生成随机字符,接收两个参数:

  • 第一个参数:随机字符的数量
  • 第二个参数:回调函数,内部传递一个出错时的 error 对象和存储字符的 buffer
crypto.randomBytes(32, (err, buffer) => {
  if (err) {
    console.log(err);
    return res.redirect("/reset");
  }
  const token = buffer.toString("hex"); // 从缓冲区中生成一个token @1
});

@1:buffer 中存储字符是十六进制,需要通过toString('hex')将十六进制的值转化为 ASCII。

例子

  • 引入 http 模块,创建一个服务器监听 3000 端口;
  • 在服务器中判断 3000 端口的路由地址:如果是根路由,则写入一段 HTML 代码(一个表单,里面含有输入框和提交按钮,表单的提交地址为/message,提交方法为POST)后返回响应给浏览器;;
const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {
  const url = req.url;
  if (url === "/") {
    // 如果url地址是/
    res.write("<html>");
    res.write("<head><title>Enter Message</title></head>");
    res.write(
      '<body><form action="/message method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'
    );
    res.write("</html>");
    return res.end(); // 返回退出代码,如果没有return则不会执行外层后面的res各个操作,就不能获取下面的Hello,Node.js页面
  }

  if (url === "/message" && method === "POST") {
    const body = [];

    // 监听data事件,当新的块(chunk)被读取时就会触发data事件,执行回调函数。函数接收一个参数块
    req.on("data", (chunk) => {
      body.push(chunk); // 将块推入到body中
    });
    req.on("end", () => {
      const parseBody = Buffer.concat(body).toString(); // 创建一个缓冲区连接到body上
      const message = parseBody.split("=")[1];
      fs.writeFileSync("message.txt", message);
    });

    res.statusCode = 302;
    res.setHeader("Location", "/");
    // res.writeHead(302, {
    //     'Location': '/'
    // })
    return res.end();
  }

  res.setHeader("Content-Type", "text/html");
  res.write("<html>");
  res.write("<head><title>First page</title></head>");
  res.write("<body><h1>Hello,Node.js</h1></body>");
  res.write("</html>");
  res.end();
});

server.listen(3000);

附录

res

响应对象可调用的常用方法:

  • setHeader:设置响应标头的单个属性,以键值对的方式修改,可调用多次

    res.setHeader("Location", "/"); // 设置地址跳转到/
    
  • writeHead:设置响应头多个属性(包括状态码、状态信息和响应标头),只调用一次,可进行链式调用

    res.writeHead(statusCode, [reasonPhrase], [headers]);
    
    res.writeHead(200, {
      "Content-Type": "text/plain",
      Location: "/",
    });
    
    • statusCode:状态码;
    • reasonPhrase(可选):状态信息;

    • headers:响应标头;

      注意:两个参数的最后一个参数是 headers 响应标头!三个参数只是方便 reasonPhrase。

    注意:setHeaderwriteHead都有的时候,setHeader的内容合会并到writeHead,合并有冲突时以writeHead的内容为准(writeHead优先级高)。

req

请求数据的常用方法:

  • on:事件监听。接收两个参数,一个是监听的事件,一个是触发的函数:

    // 监听data事件,当新的块(chunk)被读取时就会触发data事件,执行回调函数。函数接收一个默认参数,块(chunk)
    req.on("data", (chunk) => {});
    
    // 在完成读取传入的数据或请求后结束监听
    req.on("end", () => {});
    

    当遇到req.on()时,Node.js 会自动在内部添加一个新的事件监听器来管理所有的监听器,相当于一个注册表在注册了这些事件但没有执行它,然后跳过继续执行下一条。(注册未来某个时间运行但不一定现在运行的代码函数)