4Node.js Web 服务器创建和静态文件处理指南(下)

54 阅读3分钟

一. 完成六个目标,开始完成目标一

  • 以上就是一些前置的基础之后,接下来我们来完成六个目标,目标一根据url返回不同的文件
  • 我们先要准备一些静态文件
    • 新建public/index.html
    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>网页1</title>
    </head>
    <body>
      <h1>这是一个 index.html</h1>
    </body>
    </html>
    
    • 新建public/style.css
    h1 {
      color: red;
    }
    
    • 新建public/main.js
    const h1 = document.querySelector('h1')
    h1.onclick = () => {
      alert('我是 js')
    }
    
  • 搞定三个静态文件之后,我们来处理服务端,先写返回index.html,这样子我们访问http://127.0.0.1:8888/index.html就能得到一个html文件
import * as http from 'http';
import * as p from 'path';
import * as fs from 'fs';
const server = http.createServer();
const publicDir = p.resolve(__dirname, 'public')

server.on('request', (request, response) => {
  const {method, url, headers} = request
  console.log('url', url)
  switch(url) {
    case '/index.html':
      fs.readFile(p.resolve(publicDir, 'index.html'), (error, data) => {
        if(error) throw error;
        response.end(data.toString())
      });
      break;
  }
});
server.listen(8888);

  • 依样画葫芦,返回style.css和main.js,需要注意这里要设置response的Header,告诉浏览器如何响应
server.on('request', (request, response) => {
  const {method, url, headers} = request
  console.log('url', url)
  switch(url) {
    case '/index.html':
      response.setHeader('Content-Type', 'text/html; charset=utf-8');
      fs.readFile(p.resolve(publicDir, 'index.html'), (error, data) => {
        if(error) throw error;
        response.end(data.toString())
      });
      break;
    case '/style.css':
      response.setHeader('Content-Type', 'text/css; charset=utf-8');
      fs.readFile(p.resolve(publicDir, 'style.css'), (error, data) => {
        if(error) throw error;
        response.end(data.toString())
      });
      break;
    case '/main.js':
      response.setHeader('Content-Type', 'text/javascript; charset=utf-8');
      fs.readFile(p.resolve(publicDir, 'main.js'), (error, data) => {
        if(error) throw error;
        response.end(data.toString())
      });
      break;
  }
});
server.listen(8888);
  • 给html加上css和js引用,这样就能看到样式和有js
<!doctype html>
<html lang="ch">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>网页1</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>这是一个 index.html</h1>
  <script src="main.js"></script>
</body>
</html>

二. 完成任务二,处理查询参数

  • 我们给链接加上?q=1,curl -v -d "name=frank" http://localhost:8888/index.html?q=1
  • 我们需要借助url模块来实现,查询参数不影响代码逻辑
server.on('request', (request, response) => {
  const {method, url: path, headers} = request
  const {pathname, search} = url.parse(path)
  switch(pathname) {
    case '/index.html':
    ...
  }
}

三. 完成任务三:如何匹配任意文件

  • 此时我再加一个js文件,比如jquery.js并在html中引用
server.on('request', (request, response) => {
  const {method, url: path, headers} = request
  const {pathname, search} = url.parse(path)
  // response.setHeader('Content-Type', 'text/html; charset=utf-8');
  const filename = pathname.substring(1);
  fs.readFile(p.resolve(publicDir, filename), (error, data) => {
    if(error) {
      response.statusCode = 404;
      response.end('文件不存在');
    } else {
      response.end(data.toString())
    }
  });
});
server.listen(8888);

  • 此时只要url路径是和服务器的路径匹配即可访问到

四. 完成任务四:处理错误

  • 新建一个404页面
  • 完成错误逻辑
server.on('request', (request, response) => {
  const {method, url: path, headers} = request
  const {pathname, search} = url.parse(path)
  let filename = pathname.substring(1);
  if (filename==='') {
    filename = 'index.html'
  }
  response.setHeader('Content-Type', 'text/html; charset=utf-8');
  fs.readFile(p.resolve(publicDir, filename), (error, data) => {
    if(error) {
      console.log(error)
      if(error.errno===-2) {
        response.statusCode = 404;
        fs.readFile(p.resolve(publicDir, '404.html'), (error, data) => {
          response.end(data);
        })
      } else if (error.errno===-21) {
        response.statusCode = 403;
        response.end('无权查看目录内容');
      } else {
        response.statusCode = 500;
        response.end('服务器繁忙,请稍后再尝试');
      }
    } else {
      response.end(data)
    }
  });
});
server.listen(8888);
  • 注意小细节,html中图片的路径写法,如果提示下划线,在public上右键mark as root directory
  <img src="/images/404.png" alt="">

五. 目标五:处理非get请求

  • 现在index.html构造一个post请求
  <form action="/form" method="post" autocomplete="offs">
    <label><input type="text" name="username"></label>
    <button type="submit">提交</button>
  </form>
  • 我们需要阻止post请求,在index.ts中对method做过滤
  if (method!=='GET') {
    response.statusCode = 405
    response.end();
    return;
  }

六. 完成任务六: 开启缓存选项

  • 发现这次请求和上一次一样就使用缓存,通过Cache-Control做
  • 响应成功的请求头加上这句话即可
response.setHeader('Cache-Control', 'public, max-age=31536000')
  • 除了首页无法缓存,此时其它的文件都是从内存中读取

七. 对比业界优秀案例

  • http-server和node-static
  • 坚持维护三个月可以兑换webstorm一年的code