node.js后端开发

node.js

1.node.js简介

  • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。

  • Node.js底层用C++实现的,大部分模块都使用JavaScript编写,含大量的内置模块,使得程序可以脱离Apache、Nginx、IIS等web服务器,作为独立的服务器运行;

node.js优点

  1. 采用事件驱动、异步编程,为网络服务而设计。其实Javascript的匿名函数和闭包特性非常适合事件驱动、异步编程。而且JavaScript也简单易学,很多前端设计人员可以很快上手做后端设计。
  2. Node.js非阻塞模式的IO处理给Node.js带来在相对低系统资源耗用下的高性能与出众的负载能力,非常适合用作依赖其它IO资源的中间层服务
  3. Node.js轻量高效,可以认为是数据密集型分布式部署环境下的实时应用系统的完美解决方案。Node非常适合如下情况:在响应客户端之前,您预计可能有很高的流量,但所需的服务器端逻辑和处理不一定很多。

node.js 和 Java比较

  1. node.js比Java开发快,运行效率也很高,但仅仅局限于小项目,用node.js做大项目容易乱,并且JavaScript不是静态类型的语言,要运行之后才能知道类型错误,所以写大项目写着写着都不知道错哪里了
  2. Java开发较慢,但在大项目、复杂的项目中,用Java写更为清晰,而且还不容易乱,更加好维护

2.node.js安装

  1. Node.js 官方网站下载:
  2. 无脑Next
  3. 安装成功后按住win+r 输入cmd 回车。运行CMD
  4. 分别输入node -v 和 npm -v分别查看node和npm的版本号 有显示版本就表示已经安装成功

3.快速入门

快速搭建node原生服务器

var http=require("http"); //引用http模板
var server =  http.createServer(function(req,res){
    //request:请求对象  response:响应请求
    response.setHeader("content-type","text/html;charset=utf-8")
    res.end("hello world");
});
//指定服务器运行的端口
server.listen(3000,function(err){
    if(!err){  console.log("服务器启动成功")  }else{
        console.log(err);  }
});

运行后在浏览器中输入http://localhost:3000 回车 -->则会弹出hello word字样

 模块引用 如 var http=require("http");

3.1★建立服务、路径处理与响应

3.1.1让Web服务器响应和处理不同路径

var http = require('http'); //require 引用内置模块http
var url = require('url'); //require 引用内置模块url
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'}); //设置头信息

    var pathname = url.parse(req.url).pathname;  //把请求网址交给url 对象处理
    var bodyStr ="";  //定义一个变量,用来存储要输出的内容
    if(pathname==="/"){ //如果是首页
        bodyStr = 'Hello World\n';
    }else{
        bodyStr = req.url;  //如果是其他路径
    }
    res.end(bodyStr);         //输出内容
}).listen(9527, '127.0.0.1');     //绑定IP 和端口
console.log('Server running at http://127.0.0.1:9527/'); //控制台输出提示

启动这个范例的服务,然后在浏览器中输入localhost:9527和localhost:9527/other可以看到期望的效果。
在这里插入图片描述
再改变一下业务需求,首页显示不变,增加一个/about关于页面,显示一些联系信息,其他页面就提示404 not found。随着请求路径的增长,用if判断的方式对代码组织很不友好,下面改用对象来尝试一下

3.1.2可配置的路径

var http = require('http'); //require 引用内置模块http
var url = require('url');   //require 引用内置模块url
var webPath = {               //许可的路径
    "/":"Hello World\n",
    "/about":"ID:z3f\nQQ:10590916"
}
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'}); //设置文件头信息
    var pathname = url.parse(req.url).pathname;  //把请求网址交给url 对象处理
    //如果访问路径没有被webPath 指定就是Not found
    var  bodyStr  =  webPath[pathname]  ||  "Not  found!  \n"+req.url+"  was  not found on this server.";
    res.end(bodyStr);                         //输出内容
}).listen(9527, '127.0.0.1');     //绑定IP 和端口
console.log('Server running at http://127.0.0.1:9527/'); //控制台输出提示


运行代码之后,通过图中会发现之前请求的/other路径现在提示Not found,这是因为我们没有把它放到许可列表中。

在这里插入图片描述

3.2★智能处理404提示

将404错误页面放在子目录server下,这样分离的好处是,如果只改变404.html文件的提示内容,那么服务器不用做任何改动,甚至重启都不用, 便利很多,减少了维护成本。

如下图所示,该404设计,在错误提示页面显示了一些公 益信息。

在这里插入图片描述

 var msgOf404 = require('./22-5.json')
 var http = require('http'); //require 引用内置模块http
 var url = require('url');   //require 引用内置模块url
 var fs = require('fs');     //require 引用内置模块fs
 var webPath = {                       //许可的路径
           "/":"Hello World\n",
           "/about":"ID:z3f\nQQ:10590916"
  }
  var on200 = function(req, res, bodyStr){
         res.writeHead(200, {'Content-Type': 'text/plain'}); //设置文件头信息
        res.end(bodyStr);                //对客户端输出内容后结束
    }
   var on404 = function(req, res){
       fs.readFile("server/404.html", "utf-8", function (err, file) {
     //用内置fs 对象读取文件
           res.writeHead(404, {'Content-Type': 'text/html'});    //设置文件头信息
           res.write(file
                   .replace(/{url}/g, req.url)//通过替换的方式把请求网址显示出来
                   .replace(/{msg}/g, msgOf404[1+parseInt(Math.random()*3)])
               //通过替换方式随机显示1 条名言
                   , "utf-8");   //用utf-8 编码输出,这是为了和前面readFile 时的编码一致
                   res.end();    //结束输出
            });
  };
 http.createServer(function (req, res) {
         var pathname = url.parse(req.url).pathname; //把请求网址交给url 对象处理
         var bodyStr = webPath[pathname]; //如果访问路径没有被 webPath 指定会返回
undefined
           if(bodyStr){
                   on200(req, res, bodyStr); //找到许可路径就让on200 函数处理
               }else{
                   on404(req, res);  //没找到就让on404 函数处理
               }
    }).listen(9527, '127.0.0.1'); //绑定IP 和端口
 console.log('Server running at http://127.0.0.1:9527/'); //控制台输出提示

随机输出一条名人名言,事实上的项目是读取数据库或是获取到的其他信息。单独以json形式存储并且跟模板文件放到同级目录

{"1": "设计是一个发现问题的过程,而不是发现解决方案的过程——Leslie Chicoine",
	"2": "一个好的程序员应该是那种过单行线都要往两边看的人——Doug Linder", 
	"3": "解决问题大多数都很容易;找到问题出在哪里却很难——无名"}

404.html的HTML代码(server/404.html)

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>404 not found! </title>
    </head>
<body>
  <h1>404 not found! </h1>
  <p>
          对不起!没有找到你请求的路径!<br />
           {url}<br /><br />
           {msg}<br />
    </p>
<a href="/">返回首页</a>
</body>
</html>

智能的404错误页面的

在这里插入图片描述

3.3★处理不同类型的文件

node.js静态服务器准备的MIME映射表 22-6.MIME.json

{
  "txt": "text/plain",
  "css": "text/css",
  "htm": "text/html",
  "html": "text/html",
  "gif": "image/gif",
  "ico": "image/x-icon",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "js": "text/javascript",
  "json": "application/json",
  "pdf": "application/pdf",
  "png": "image/png",
  "svg": "image/svg+xml",
  "swf": "application/x-shockwave-flash",
  "tiff": "image/tiff",
  "wav": "audio/x-wav",
  "wma": "audio/x-ms-wma",
  "wmv": "video/x-ms-wmv",
  "xml": "text/xml"
}

22-6.config.js

范例中使用了一个cfg.index的值,cfg是另外定义的一个服务器配置文件,一个服务器总是有很多参数配置的,比如默认首页、默认的主目录等

/*
exports.root = "webroot/";  //主目录
exports.index = "index.html"; //默认主页
*/
exports.root = "../03/";  //主目录
exports.index = "3-1.html"; //默认主页

处理不同类型的文件

var cfg = require('./22-6.config.js');
var MIME = require('./22-6.MIME.json'); //require 引用用户自定义模块
var onFiles = function(req, res){
var pathname = url.parse(req.url).pathname;  //把请求网址交给url 对象处理
pathname = path.normalize(pathname.replace(/\.\./g, ""));     //处理父路径
    if(pathname==="\\"){  //如果是根目录就用设置的默认首页
                pathname = cfg.index;
       }
    var filepath = cfg.root+pathname;    //找到真实地址
    
    /*    on404  前面已经编写 */

    
    path.exists(filepath, function(exists){     //检查是否存在该文件
        if(! exists){
            on404(req, res);      //如果不存在就提示404
           }else{
             fs.readFile(filepath, "binary", function(err, file){ //读取文件
                if(err){  //如果读取过程失败则返回500 服务端程序错误
                 res.writeHead(500, {"Content-Type":"text/plain"});
                 res.end(err);
                 return;
                }
                var ext = path.extname(filepath);      //获取文件后缀
                 ext = ext ? ext.slice(1) : 'unknown';
                 //对于没有后缀的当作未知文件
                 var  contentType  =MIME[ext]||"application/octet-stream";
                 //对没定义的当作二进制处理
                 res.writeHead(200, {"Content-Type":contentType});
                 res.write(file, "binary");
                 res.end();
             });
           }
     });
};

http.createServer(function (req,res) {
    res.setHeader("Server","z3f nodejs web server/0.1")
    onFiles(req,res);
}).listen(9527,'127.0.0.1');//绑定IP 和端口

模块

模块分为两大类

  • 核心模块

    由node引擎提供的模块,核心模块的标识就说模块的名字

    如 var fs = require(“fs”);

  • 文件模块

    由用户自己创建的模块,文件模块的标识就是文件的路径(相对路径,绝对路径)

    var md = require("./module");

方法exports

​ require:函数,用来引入外部的模块

​ function:回调函数

​ exports:向外部暴露变量和方法

​ module:代表的是当前模块本身 exports就是module的属性

exports 和 module.exports对s比

  1. exports只能使用 . 的方式来向外暴露内部变量 如exports.x = “xxx”
  2. odule.exports可以通过 . 的形式,也可以直接赋值 如:module.exports = {}
//Demo01.js
var d = require("./Demo02");
console.log(d);
/*========分隔符=========*/ 

//Demo02.js
exports.x = "我是x";
exports.y = "我是y";

/*
输出结果{ x: '我是x', y: '我是y'}
*/

4.NPM

对于Node而言,NPM帮助其完成了第三方模块的发布、安装和依赖等。借助NPM,Node与第三方模块之间形成了很好的一个生态系统。它可以让开发者能够更加轻松的共享代码和共用代码片段,并且通过 npm 管理你分享的代码也很方便快捷和简单,不过有的时候项目依赖过多,造成路径过深,超过了操作系统的文件深度限制。

安装后node,将会自动安装npm;可以输入node -v ,npm - v 查看版本号

输入sudo npm install npm -g 可以更新npm的版本

4.1 npm命令

  1. npm –v 查看版本
  2. npm remove 包名 –删除模块
  3. npm search 包名 –搜索模块
  4. npm update 包名 –更新模块
  5. npm install –下载当前项目所依赖的包
  6. npm install 包名 –在当前目录安装包
  7. npm install 包名 –g –全局安装
  8. npm install 文件路径 –本地安装
  9. npm install 包名 --save –安装 (常用)
  10. npm install 包名 –registry(地址) –从镜像源安装
  11. npm config set registry(地址) –设置镜像源

本地安装local

  1. 把安装包放在./node_modules下,如果没有node_modules 目录,则会在当前执行的npm命令中的目录下生成node_modules目录
  2. 可以用require()方法来引入安装包

全局安装global

  1. 把安装包放在/usr/locat中或者放在node的安装目录中

  2. 可以直接在命令行中使用

    1.将通过npm下载的包全都放在node_modules文件中,直接通过包名引入
    2.当使用node模块时,首先在node_modules中寻找模块,如果找不着则会去上一级目录寻找,直到找到为止;直到根目录没有时则会报错。
    

5.Buffer缓冲

在Node.js中,Buffer对象用于以字节序列的形式表示二进制数据。也可以直接通过Buffer来创建内存中的空间。

方法

写入操作

  1. 向缓冲区中写入字符串– buf.write(string[, offset[, length]][, encoding])

    • string 要写入的[字符串]buf
    • offset 开始写入之前要跳过的字节数string默认值: 0
    • length 要写入的最大字节数(写入的字节数不会超过buf.length - offset)。默认值: buf.length - offset
    • encoding 的字符编码string默认值: 'utf8'
    • 返回:写入的字节数。
  2. 替换指定索引位置的数据– buf[index]

    • index [<整数>]
  3. 指定值填入到缓冲区的指定位置– buf.fill(value[, offset[, end]][, encoding])

    • value [<缓冲区>] 用来填充的值buf
    • offset 开始填充之前要跳过的字节数buf默认值: 0
    • end 在哪里停止填充buf(不包括在内)。默认值: [buf.length]
    • encodingString value if 的编码value是一个字符串。 默认值: 'utf8'
    • 返回:对的引用buf

读取操作

  1. 将缓冲的内容,转换为字符串返回– buf.toString([encoding[, start[, end]]])
    • encoding要使用的字符编码。默认值: 'utf8'
    • start开始解码的字节偏移量。默认值: 0
    • end 停止解码的字节偏移量(不包括)。 默认值: [buf.length]
    • 返回:string
  2. 读取缓冲区指定索引的内容– buf[index]

其他操作

  1. 复制缓冲区– buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
  2. 对缓冲区切片– buf.slice([start[, end]])
  3. 拼接缓冲区– Buffer.concat(list[, totalLength])
var buf = Buffer.from("耐不住的寂寞,吃不着的豆腐");
console.log(buf.toString());

6.fs 文件系统

该fs模块提供了一种API,用于以与标准POSIX函数紧密相似的方式与文件系统进行交互。

要使用模块const fs = require('fs');

6.1简单文件写入

  1. fs.writeFile(file, data[, options], callback)
  2. fs.writeFileSync(file, data[, options])
    参数:
    file 文件路径
    data 被写入的内容,可以是String或Buffer
    options 对象,包含属性(encoding、mode、flag)
    callback 回调函数

6.1简单文件读取

  • fs.readFile(file[, options], callback)
  • fs.readFileSync(file[, options])
  • 参数:
  • file 文件路径或文件描述符

6.2同步文件写入

  1. fs.writeSync(fd, buffer, offset, length[, position])
  2. fs.writeSync(fd, data[, position[, encoding]])
    • 要完成同步写入文件,先需要通过openSync()打开文件来获取一个文件描述符,然后在通过writeSync()写入文件。
      参数:
    • fd 文件描述符,通过openSync()获取
    • data 要写入的数据(String 或 Buffer)
    • offset buffer写入的偏移量
    • length 写入的长度
    • position 写入的起始位置
    • encoding 写入编码

6.2同步文件读取

  • fs.write(fd, buffer, offset, length[, position], callback)

  • 要使用异步写入文件,先需要通过open()打开文件,然后在回调函数中通过write()写入。
    参数:

    • fd 文件描述符
    • buffer 读取文件的缓冲区
    • offset buffer的开始写入的位置
    • length 要读取的字节数
    • position 开始读取文件的位置

6.3异步文件写入

  • fs.read(fd, buffer, offset, length, position, callback)
  • fs.write(fd, data[, position[, encoding]], callback)
  • 要使用异步写入文件,先需要通过open()打开文件,然后在回调函数中通过write()写入。
  • 参数:
    • fd 文件描述符
    • data 要写入的数据(String 或 Buffer)
    • offset buffer写入的偏移量
    • length 写入的长度
    • position 写入的起始位置
    • encoding 写入编码

6.3异步文件读取

  • fs.read(fd, buffer, offset, length, position, callback)
  • 参数:
    • fd 文件描述符
    • buffer 读取文件的缓冲区
    • offset buffer的开始写入的位置
    • length 要读取的字节数
    • position 开始读取文件的位置
    • callback 回调函数 参数err , bytesRead , buffer

6.4流式文件写入

  • 往一个文件中写入大量数据时,最好的方法之一是使用流。

  • 若要将数据异步传送到文件,首需要使用以下语法创建一个Writable对象:

    • fs.createWriteStream(path[, options])
      path 文件路径
    • options {encoding:"",mode:"",flag:""}-
  • 一旦你打开了Writable文件流,就可以使用write()方法来写入它,写入完成后,在调用end()方法来关闭流。

6.4流式文件读取

  • 从文件中读取大量的数据时,最好的方法之一就是流式读取,把文件作为Readable流的形式打开

  • 要从异步从文件传输数据,首先需要通过以下语法创建一个Readable流对象:

    • fs.createReadStream(path[, options])
      • path 文件路径
      • options {encoding:"",mode:"",flag:""}
  • 当你打开Readable文件流以后,可以通过readable事件和read()请求,或通过data事件处理程序轻松地从它读出。

文件指令操作

  • 验证路径是否存在
    • fs.exists(path,callback)
    • fs.existsSync(path)
  • 获取文件信息
  • fs.stat(path, callback)
  • fs.statSync(path)
  • 删除文件
  • fs.unlink(path, callback)
  • fs.unlinkSync(path)
  • 列出文件
    • fs.readdir(path[, options], callback)
    • fs.readdirSync(path[, options])
  • 截断文件
    • fs.truncate(path, len, callback)
    • fs.truncateSync(path, len)
  • 建立目录
    • fs.mkdir(path[, mode], callback)
    • fs.mkdirSync(path[, mode])
  • 删除目录
  • fs.rmdir(path, callback)
  • fs.rmdirSync(path)
  • 重命名文件和目录
    • fs.rename(oldPath, newPath, callback)
    • fs.renameSync(oldPath, newPath)
  • 监视文件更改写入
    • fs.watchFile(filename[, options], listener)

flags参数

var fs = require("fs");// 异步读取
fs.readFile("input.txt", function (err1, err2) {
   if (err1) {
       return console.error(err1);
   }
   console.log("异步读取: " + err2.toString());
});
// 同步读取
var err2 = fs.readFileSync('input.txt');
console.log("同步读取: " + err2.toString());
console.log("程序执行完毕。");

6.5 ★处理上传图片

安装node-formidable模块

Node.js有相当多的开源模块(或者叫插件),在实际项目中,很多时候需要赶时间或是没有能力或者精力去造轮子时就需要借助别人的力量,在无法保证功能安全性和完整性时最佳的选择就是选择成熟的第三方产品

在这里插入图片描述

为了便于引用,需要设置一下环境变量NODE_PATH,这是为了编程时像系统内置模块一样引用,否则可能会提示模块无法找到

在这里插入图片描述

node-formidable处理上传图片

var http = require('http');         //require 引用内置模块http
var url = require('url');           //require 引用内置模块url
var fs = require('fs');              //require 引用内置模块fs
var path = require('path');         //require 引用内置模块path
var msgOf404 = require('./22-5.json');   //require 引用自定义模块
var MIME = require('./22-8.MIME.json');  //require 引用自定义模块
var cfg = require('./22-11.config.js');  //require 引用自定义模块
var formidable = require('formidable');  //require 引用第三方模块
var on404 = function(req, res){
    fs.readFile("server/404.html", "utf-8", function (err, file) {
        //用内置fs 对象读取文件
        res.writeHead(404, {'Content-Type': 'text/html'}); //设置文件头信息
        res.write(file
                .replace(/{url}/g, req.url)//通过替换的方式把请求网址显示出来
                .replace(/{msg}/g, msgOf404[1+parseInt(Math.random()*3)])//通过替换随机显示一条名言
            , "utf-8"); //用utf-8 编码输出,这是为了和前面readFile 的编码一致
        res.end();  //结束输出
   });
};
var upload = function(req, res){
    var form = new formidable.IncomingForm()
    var fields=[], files=[], fieldsDATA={}, filesDATA={}
    form.uploadDir = cfg.root;             //指定目录
    form.keepExtensions=true;                  //保持上传文件的后缀
    form.maxFieldsSize = 2 * 1024 * 1024;    //最大限制2MB
    form.on('field', function(field, value) {       //监听有内容时
        fields.push([field, value]);               //获取表单字段信息
    })
        .on('file', function(field, file) {      //监听有上传文件时
            files.push([field, file]);         //获取表单上传文件信息
        })
        .on('end', function() {                     //监听完成时
            console.log('-> upload done');    //控制台输出提示,可去掉
            for (var i=0; i<fields.length; i++){
                fieldsDATA[fields[i][0]] = fields[i][1];//数组转对象
            }for (var i=0; i<files.length; i++){
                filesDATA[files[i][0]]=files[i][1]; //数组转对象
            }
            var oldf = filesDATA.upfile.path;
            var newf = oldf.replace(/(\w+)\./, "z3f.").replace(/\\/g, "/");
            fs.renameSync(oldf, newf);          //异步修改文件名
            res.writeHead(200, {'content-type': 'text/html'});
            res.write('TEMP Name:'+oldf+'<br />');
            res.write('NEW Name:'+newf+'<br />');
            res.write('<br /><img src="'+newf.substr(newf.lastIndexOf("/"))+'"/>');
            res.end('ok');
        });
    form.parse(req); //主要方法
};
var onFiles = function(req, res){
    var pathname = url.parse(req.url).pathname;    //把请求网址交给url 对象处理
    pathname = path.normalize(pathname.replace(/\.\./g, ""));
    //处理父路径
    if(pathname==="\\"){                //如果是根目录就用设置的默认首页
        pathname = cfg.index;
    }
    if(pathname==="\\upload" && req.method.toLowerCase() == 'post'){
        upload(req, res);
        return;
    }
    var filepath = cfg.root+pathname;                 //找到真实地址
    path.exists(filepath, function(exists){           //检查是否存在该文件
        if(! exists){
            on404(req, res);                      //如果不存在就提示404
        }else{
            fs.readFile(filepath, "binary", function(err, file){//读取文件
                if(err){       //如果读取过程失败则返回500 服务端程序错误
                    res.writeHead(500, {"Content-Type": "text/plain"});
                    res.end(err);
                    return;
                }
                var ext = path.extname(filepath);         //获取文件后缀
                ext = ext ? ext.slice(1) : 'unknown'; //去点,对于没有后缀的当作未知文件
                var contentType = MIME[ext]||"application/octet-stream";      //对没定义的当作二进制处理
                res.writeHead(200, {"Content-Type":contentType});
                res.write(file, "binary");
                res.end();     //完成输出,结束对浏览器的输出
            });
        }
    });
};
http.createServer(function (req, res) {
    res.setHeader("Server", "z3f nodejs web server/0.1");
    onFiles(req, res);
}).listen(9527, '127.0.0.1');                              //绑定IP 和端口
console.log('Server running at http://127.0.0.1:9527/'); //控制台输出提示

22-7.config.js的配置

exports.root = "/webroot";
exports.index = "upload.html";

而upload.html的代码也很简单

<form method="post" action="/upload" enctype="multipart/form-data">
 选择文件:<input type="file" name="upfile" /><input type="submit"
          value="上传" /><br /><br />
</form>

7.get、post请求

HTTP设定八种请求(八大动作)

8种方法:options、head、get、post、put、delete、trace、connect

常用:get、post

什么时候用get
    1.单纯获取数据的时
    2.请求非敏感数据时
常见的GET请求:
   1.浏览器地址栏输入网址时
   2.请求外部资源的html标签,例如:<img> <a> <link> <script>,且无法手动更改
   3.发送Ajax时若没有请求的方式,则用GET方式
   4.form表单提交时,若没有指明方式,默认使用GET

什么时候用post
    1.传送相对敏感数据时
    2.请求的结果有持续性的副作用,例如:传递的数据要写入数据库时
        备注:使用了POST不代表的绝对的安全
常见的POST请求:
   1.发送Ajax时,明确指出了使用POST方式时
   2.form表单提交时明确指出使用POST方式

8.HTTP

http协议:超文本传输协议,基于TCP/IP应用通信协议

作用:规定服务器和客户端传递信息的规则(统称报文,分为:请求报文、响应报文)

请求报文:客户端发送给服务器

响应报文:服务器发送给客户端

特点:无状态,现在cookie解决了无状态的问题(早期网页开发时,用cookie解决,现在是cookie和session配合使用)

版本:

  • http 1.0 (老版本) ---------- 不支持长连接
  • http 1.1 (主流版本)--------- 优点:支持长连接,弊端:同时发送资源的数量过小。
  • http 2.0 (最新版) ---------- 同时发送资源的数量稍有提升。

报文的组成:

  1. 报文首行
  2. 报文头
  3. 空行(仅仅作为一个分割)
  4. 报文体

GET报文

通过form表单发送get请求

GET http://localhost:3000/?name=kobe&password=123 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://localhost:63347/0719_node/demo.html?_ijt=tphpp47dag8jevtqrnq44blv6p
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a
空行
空行

请求首行

GET http://localhost:3000/?name=kobe&password=123 HTTP/1.1
-请求方式 协议名://主机地址:端口号/?urlencoded编码形式的参数 协议名/版本

请求报文头

Host: localhost:3000
   --发送请求的目标主机:主机名:端口号
Connection: keep-alive
   --浏览器告诉服务器,浏览器支持长连接。
Pragma: no-cache
   -- 不走缓存
Cache-Control: no-cache
   -- 不走缓存(强缓存)
Upgrade-Insecure-Requests: 1
   -- 浏览器告诉服务器可以使用 https或http1.1
DNT: 1
   -- 浏览器告诉服务器:禁止跟踪。最终是否跟踪,还得看服务器是否配合。
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
   -- 用户代理:之前该字段用于判断用户的浏览器品牌以及版本,但是现在不好用了。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
   -- 浏览器能够接收资源的类型及优先级,优先级不写默认是1,1的优先级是最高的。
Referer: http://localhost:63347/0719_node/demo.html?_ijt=tphpp47dag8jevtqrnq44blv6p
   -- 本次请求是“站”在哪里发出去的。 1.防盗链。 2.广告计费
Accept-Encoding: gzip, deflate, br
   -- 浏览器告诉服务器,浏览器所能接受的压缩文件类型。
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
   -- 浏览器告诉服务器,浏览器所能支持的语言种类,及权重。
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a
   -- Webstorm给你种下的cookie

请求体

空行(表示GET请求没有请求体)

POST请求报文

通过form表单发送post请求

POST http://localhost:3000/ HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 22
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:63347
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://localhost:63347/0719_node/day04/5.http%E6%8A%A5%E6%96%87&%E7%8A%B6%E6%80%81%E7%A0%81/%E6%BC%94%E7%A4%BA%E9%98%B2%E7%9B%97%E9%93%BE.html?_ijt=v73gogoe0uaatcie38ma6l7gso
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a
空行
name=kobe&password=123

请求报文首行

 POST http://localhost:3000/ HTTP/1.1

请求报文头

Host: localhost:3000
Connection: keep-alive
【Content-Length: 22】
    -- 返回数据的长度
Pragma: no-cache
Cache-Control: no-cache
【Origin: http://localhost:63347】
    -- 精简版的Referer  1.防盗链。 2.广告计费
Upgrade-Insecure-Requests: 1
DNT: 1
【Content-Type: application/x-www-form-urlencoded】
    --浏览器告诉服务器,发送数据的类型
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://localhost:63347/0719_node/demo.html?_ijt=r08g7l67qsmghv05cf7mphidka
    -- “站”在哪里发出去的请求(源站)  1.防盗链。 2.广告计费
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a

空行

空行(就一个简单的空行,分隔作用,没有别的含义)

请求报文体

name=kobe&password=123

get和post

1.form表单的 post请求和get请求 参数均已urlencoded形式进行编码
2.get请求将urlencoded编码的参数放入请求地址携带给服务器,所以称之为:查询字符串参数。
3.post请求将urlencoded编码的参数放入请求体,所以称之为:请求体参数。

响应报文(浏览器看的)

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-eoX0dku9ba8cNUXvu/DyeabcC+s"
Date: Fri, 01 Nov 2019 08:24:19 GMT
Connection: keep-alive
空行
ok

报文首行

 HTTP/1.1 200 OK
   --协议名/协议版本 状态码 

报文头

X-Powered-By: Express 
    --服务器所采用的框架(尽量不要让用户知道服务器具体采用的技术)
Content-Type: text/html; charset=utf-8
    --告诉浏览器返回资源的类型及编码格式
Content-Length: 2
    --返回数据的长度
ETag: W/"2-eoX0dku9ba8cNUXvu/DyeabcC+s"
    --协商缓存必要字段
Date: Fri, 01 Nov 2019 08:24:19 GMT
    --响应的日期+时间
Connection: keep-alive
    --服务器告诉浏览器,下次请求时,或许会采用长连接。

报文体

ok

HTTP状态码

状态码分类

  • 1xx : 服务器已经收到了本次请求,但是还需要进一步的处理才可以。
  • 2xx : 服务器已经收到了本次请求,且已经分析、处理等…最终处理完毕!
  • 3xx : 服务器已经接收到了请求,还需要其他的资源,或者重定向到其他位置,甚至交给其他服务器处理。
  • 4xx :一般指请求的参数或者地址有错误, 出现了服务器无法理解的请求(一般是前端的锅)。
  • 5xx :服务器内部错误(不是因为请求地址或者请求参数不当造成的),无法响应用户请求(一般是后端人员的锅)。

常见状态码

  • 200 :成功(最理想状态)
  • 301 :重定向,被请求的旧资源永久移除了(不可以访问了),将会跳转到一个新资源,搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址;
  • 302 :重定向,被请求的旧资源还在(仍然可以访问),但会临时跳转到一个新资源,搜索引擎会抓取新的内容而保存旧的网址。
  • 304 :请求资源重定向到缓存中(命中了协商缓存)。
  • 404 :资源未找到,一般是客户端请求了不存在的资源。
  • 500 :服务器收到了请求,但是服务器内部产生了错误。
  • 502 :连接服务器失败(服务器在处理一个请求的时候,或许需要其他的服务器配合,但是联系不上其他的服务器了)。

TCP三握四挥

进行TCP(协议)连接,三次握手(根据上一步请求回来的ip地址,去联系服务器)

  • 第一次握手:由浏览器发给服务器,我想和你说话,你能“听见”嘛?
    
  • 第二次握手:由服务器发给浏览器,我能听得见,你说吧!
    
  • 第三次握手:由浏览器发给服务器,好,那我就开始说话。
    

断开TCP连接,四次挥手(确保数据的完整性)

  •   第一次挥手:由浏览器发给服务器,我的东西接受完了,你断开吧。
    
  •   第二次挥手:--由服务器发给浏览器,我还有一些东西没接收完,你等一会,我接收好了且验证好了我告诉你
    
  •              --由服务器发给浏览器,我的东西接收完了,但是你还得等一会,我要验证数据的完整性,验证完了我告诉你。
    
  •   第三次挥手:由服务器发给浏览器,我接收完(验证完)了,你断开吧。
    
  •   第四次挥手:由浏览器发给服务器,好的,那我断开了。
    

备注:为什么要握手三次,挥手四次

​ 握手之前,还没有进行数据的传输,确保挥手就可以了

​ 挥手之前,正在进行数据的传输,为了确保数据的完整性,必须多经历一次验证(继续接受)

ejs模板

const express = require('express')

const app = express()
//让你的服务器知道你在用哪一个模板引擎-----配置模板引擎
app.set('view engine','ejs')
//让你的服务器知道你的模板在哪个目录下,配置模板目录
app.set('views','./haha')

//如果在express中基于Node搭建的服务器,使用ejs无需引入。
app.get('/show',function (request,response) {
  let personArr = [
    {name:'peiqi',age:4},
    {name:'suxi',age:5},
    {name:'peideluo',age:6}
  ]
  response.render('person',{persons:personArr,a:1})
}) //创建person.ejs文件才能生效。

app.listen(3000,function (err) {
  if (!err) console.log('服务器启动成功了')
  else console.log(err)
})
ejs语法
1.<% %> 里面能写任意的js代码,但是不会向浏览器输出任何东西
2.<%- %> 能够将后端传递过来的对象指定key所对应的value渲染页面,相对安全
3.<%= %> 能够将后端传递过来的对象指定key所对应的value渲染页面,相对不安全

3.<%= %>能够将后端传递过来对象
  • 9
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值