静态与动态服务器

2,622 阅读7分钟

静态服务器

定义

静态网页是指存放在服务器文件系统中实实在在的HTML文件。当用户在浏览器中输入页面的URL,然后回车,浏览器就会将对应的html文件下载、渲染并呈现在窗口中。早期的网站通常都是由静态页面制作的

原理

静态服务器没有请求数据库。当你输入一个url时,这个url可能对应服务器上的一个资源(文件)也可能对应一个目录。 因此服务器会对这个url进行分析,针对不同的情况做不同的事。如果这个url对应的是一个文件,那么服务器就会返回这个文件。如果这个url对应的是一个文件夹,那么服务器会返回这个文件夹下包含的所有子文件/子文件夹的列表。同时我们在获取到后缀之后就可以根据后缀来设置它的ContentType

实例代码1

  console.log(
    "大哥,有人过来询问qq.com的路径啦!路径(带查询参数)为:" + pathWithQuery
  );

  response.statusCode = 200;

  // 设置默认的首页
  const requestPath = path === "/" ? "/index.html" : path;
  // 检查.的索引
  const index = requestPath.lastIndexOf(".");
  // 裁切得到.后面的路径, 即文件类型
  const suffix = requestPath.substring(index);
  // 使用hash哈希表的数据结构,映射得到文件类型
  const filesType = {
    ".html": "tex/html",
    ".css": "text/css",
    ".js": "text/javascript",
    ".png": "image/png",
    ".jpg": "image/jpeg"
  };
  // 设置响应头的文件类型
  response.setHeader(
    "Content-Type",
    `${filesType[suffix]} || 'text/html'; charset=utf-8`
  );
  let content;
  try {
    content = fs.readFileSync(`./public${requestPath}`);
  } catch (error) {
    content = "该文本不存在";
    response.statusCode = 404;
  }
  response.write(content);
  response.end();

动态服务器

定义

动态网页是相对于静态网页而言的。当浏览器请求服务器的某个页面时,服务器根据当前时间、环境参数、数据库操作等动态的生成HTML页面,然后在发送给浏览器(后面的处理就跟静态网页一样了)。很明显,动态网页中的“动态”是指服务器端页面的动态生成,相反,“静态”则指页面是实实在在的、独立的文件。

原理

动态服务器请求了数据库。采用动态网页技术的网站可以实现更多的功能,如用户注册、用户登录、在线调查、用户管理、订单管理等等;动态网页实际上并不是独立存在于服务器上的网页文件,只有当用户请求时服务器才返回一个完整的网页。

静态VS动态

  • 对于一个业务稳定,页面变化频率不大的公司来说,静态网站是一个很好的选择
  • 对于一个公司规模比较大的公司来说,经常会有动态信息要发布给浏览者,使用动态网站的后台来管理网站信息。

Cookie

定义

Cookie是服务器发给浏览器的一段字符串,浏览器必须保存这个Cookie,除非用户删除。之后,用户发起任何二级域名请求,浏览器必须附上Cookie

例如: 电影院检票员,根据电影票来决定用户是否能进入电影院。Cookie就是电影票,有Cookie就是登录了,没有Cookie就是没有登录。

Session

定义

由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session

例如: 比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书

原理: 把用户信息放在服务的X里,再给信息一个随机id,把随机id发给浏览器,后端下次读取id时,通过x[id]获取用户信息。因为id很长,而且随机,所以用户无法篡改id。这里的X是文件,不能用内存,因为断电内存就清空了,这个X就是session

实例代码2

目标0: 以json文件搭建数据库

结构:

[
{id:1, name: "tab", password: "aaaa", age: "1"},
{id:1, name: "frank", password: "bbbb", age: "2"}
]

读取数据

// 服务器读取数据文件,转化为字符串
const string = fs.readFileSync('./database/users.json').toString()
// 把json类型的字符串转为为JS类型的数组,即反序列化
const userArray = JSON.parse(string)

存储数据

// 新建数据,push到JS类型的数组里
const userQ = { id: 4, name: "tony", password: "tttt" };
array.push(userQ);

// 使用JSON.stringify转化为json类型的字符串,即序列化
const string = JSON.stringify(array);
// 服务区读写数据文件,存储新的信息
fs.writeFileSync("./database/user.json", string);

目标1:实现用户注册功能

前端写form,监听事件,发送POST请求

<body>
    <form id="registerForm">
      <div>
        <label>用户名 <input type="text" name="name"/></label>
      </div>

      <div>
        <label>密码 <input type="password" name="password"/></label>
      </div>

      <div>
        <button type="submit">提交</button>
      </div>
    </form>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
      const $form = $("#registerForm");
      $form.on("submit", e => {
        e.preventDefault();
        const name = $form.find("input[name=name]").val();
        const password = $form.find("input[name=password]").val();

        $.ajax({
          method: "POST",
          url: "/register",
          contentType: "text/json;charset=UTF-8",
          data: JSON.stringify({ name, password })
        }).then(
          () => {
            alert("恭喜你,注册成功");
            location.href = "/login.html";
          },
          () => {}
        );
      });
    </script>
  </body>

后端接受POST请求,读取请求体中的namepassword,写入数据库

if (path === "/register" && method === "POST") {
    response.setHeader("Content-Type", "text/html;charset=UTF-8");
    const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));

    const array = [];
    // 分段监听数据上传事件,push到空数组
    request.on("data", chunk => {
      array.push(chunk);
    });
    // 如果请求的数据上传结束,打印出数据并给出响应
    request.on("end", () => {
      const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string);
      console.log(obj.name);
      console.log(obj.password);
      const lastUser = usersArray[usersArray.length - 1];
      const newUser = {
        // id 为最后一个用户的id +1
        id: lastUser ? lastUser.id + 1 : 1,
        name: obj.name,
        name: obj.password
      };
      usersArray.push(newUser);
      fs.writeFileSync("./database/user.json", JSON.stringify(usersArray));

      response.end();
    });
  } 

目标2:实现用户登录功能

新增sign.html网页,用户用来提交用户名和密码,然后跳转到新增home.html网页。前端写form,监听事件,发送POST请求

<body>
    <h1>登录页面</h1>
    <form id="logInForm">
      <div>
        <label>用户名 <input type="text" name="name"/></label>
      </div>

      <div>
        <label>密码 <input type="password" name="password"/></label>
      </div>

      <div>
        <button type="submit">登录</button>
      </div>
    </form>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
      const $form = $("#logInForm");
      $form.on("submit", e => {
        e.preventDefault();
        const name = $form.find("input[name=name]").val();
        const password = $form.find("input[name=password]").val();

        $.ajax({
          method: "POST",
          url: "/login",
          contentType: "text/json;charset=UTF-8",
          data: JSON.stringify({ name, password })
        }).then(
          () => {
            alert("登录成功");
            location.href = "/home.html";
          },
          () => {}
        );
      });
    </script>
  </body>

后端接受POST请求,读取请求体中的namepassword, 遍历数据库数据进行匹配, 得出结果

 if (path === "/login" && method === "POST") {
    response.setHeader("Content-Type", "text/html;charset=UTF-8");
    const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));

    const array = [];
    // 分段监听数据上传事件,push到空数组
    request.on("data", chunk => {
      array.push(chunk);
    });
    // 如果请求的数据上传结束,打印出数据并给出响应
    request.on("end", () => {
      const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string);

      const checkResult = usersArray.find(
        user => user.name === obj.name && user.password === obj.password
      );

      if (checkResult === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type", "text/json;charset=UTF-8");
        response.end(`{"errorCode": 4001}`);
      } else {
        response.statusCode = 200;
        response.end();
      }
    });
  } else if (path === "/home.html") {
    response.end("home");
  } 

目标3:标记用户已登录,显示用户名

设置Cookie, home.html渲染前获取user 信息,有则显示用户名,没有则提示登录。

 if (path === "/login" && method === "POST") {
    response.setHeader("Content-Type", "text/html;charset=UTF-8");
    const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));

    const array = [];
    // 分段监听数据上传事件,push到空数组
    request.on("data", chunk => {
      array.push(chunk);
    });
    // 如果请求的数据上传结束,打印出数据并给出响应
    request.on("end", () => {
      const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string);

      const checkResult = usersArray.find(
        user => user.name === obj.name && user.password === obj.password
      );

      if (checkResult === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type", "text/json;charset=UTF-8");
        response.end(`{"errorCode": 4001}`);
      } else {
        response.statusCode = 200;
        response.setHeader("Set-Cookie", `user_id=${checkResult.id}; HttpOnly`);
        response.end();
      }
    });
  } else if (path === "/home.html") {
    const cookie = request.headers["cookie"];
    console.log(cookie);
    let userId;
    try {
      userId = cookie
        .split(";")
        .filter(s => s.indexOf("user_id=") >= 0)[0]
        .split("=")[1];
    } catch (error) {}
    console.log(userId);
    if (userId) {
      const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));
      const user = usersArray.find(user => user.id.toString() === userId);
      const homeHtml = fs.readFileSync("./public/home.html").toString();
      let string;
      if (user) {
        string = homeHtml
          .replace("{{loginStatus}}", "已登录")
          .replace("{{user.name}}", user.name);
      } else {
        string = homeHtml
          .replace("{{loginStatus}}", "未登录")
          .replace("{{user.name}}", "");
      }
      response.write(string);
    } else {
      const homeHtml = fs.readFileSync("./public/home.html").toString();
      const string = homeHtml
        .replace("{{loginStatus}}", "未登录")
        .replace("{{user.name}}", "");
      response.write(string);
    }
    response.end();

目标4:防止篡改userId

目标3的代码有一个BUG,用户可以通过开发者工具或者JS篡改useId 思路一是加密,将userId加密后发送给前端,后端读取返回的useId时解密, 使用Json Web Token。思路二是把信息隐藏在服务器中, 使用session

 const session = JSON.parse(fs.readFileSync("./session.json").toString());
  console.log("有个傻子发请求过来啦!路径(带查询参数)为:" + pathWithQuery);

  if (path === "/login" && method === "POST") {
    response.setHeader("Content-Type", "text/html;charset=UTF-8");
    const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));

    const array = [];
    // 分段监听数据上传事件,push到空数组
    request.on("data", chunk => {
      array.push(chunk);
    });
    // 如果请求的数据上传结束,打印出数据并给出响应
    request.on("end", () => {
      const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string);
      const checkResult = usersArray.find(
        user => user.name === obj.name && user.password === obj.password
      );
      if (checkResult === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type", "text/json;charset=UTF-8");
        response.end(`{"errorCode": 4001}`);
      } else {
        response.statusCode = 200;
        const random = Math.random();
        session[random] = { user_id: checkResult.id };
        console.log(session[random]);
        console.log(session);
        fs.writeFileSync("./session.json", JSON.stringify(session));
        response.setHeader("Set-Cookie", `session_id=${random}; HttpOnly`);
      }
      response.end();
    });
  } else if (path === "/home.html") {
    const cookie = request.headers["cookie"];
    let sessionId;
    try {
      sessionId = cookie
        .split(";")
        .filter(s => s.indexOf("session_id=") >= 0)[0]
        .split("=")[1];
    } catch (error) {}
    console.log(sessionId);
    if (sessionId && session[sessionId]) {
      const userId = session[sessionId].user_id;
      console.log(`useId:${userId}`);
      const usersArray = JSON.parse(fs.readFileSync("./database/user.json"));
      const user = usersArray.find(user => user.id === userId);

      const homeHtml = fs.readFileSync("./public/home.html").toString();
      let string = "";
      if (user) {
        string = homeHtml
          .replace("{{loginStatus}}", "已登录")
          .replace("{{user.name}}", user.name);
      }
      response.write(string);
    } else {
      const homeHtml = fs.readFileSync("./public/home.html").toString();
      const string = homeHtml
        .replace("{{loginStatus}}", "未登录")
        .replace("{{user.name}}", "");
      response.write(string);
    }
    response.end();
  }

更多信息

点击访问完整代码

使用Node.js 搭建静态服务器

关于实现一个Node.js静态服务器你所需要知道的ALL

静态网页与动态网页的区别