node HTTP协议搭建服务

总结

浏览器(客户端)-> 服务端 交互

一、浏览器(客户端)-> 服务端 交互

   发起请求: 请求报文
   响应请求: 响应报文

   报文是:字符串

请求报文对象:request

二 、请求报文对象:request

  • 请求行:
    • 请求类型:(get、post) 后期还会接触到其他的请求类型 request.method
    • 请求url:在域名/网址之后的从/开始的后面的部分就是请求url request.url
    • http协议版本号:http/1.1 request.httpVersion
  • 请求头:request.headers 返回的是一个对象 let {host,connection} = request.headers
    每一个网址中请求的头都有一些差异,但是会有一些公共的头信息.[参考课件]
    后期自己搭建服务的时候,可以自行添加自定义头信息
  • 空行
  • 请求体:
    区分于请求方式
    • 如果是get请求,则没有请求体

      其他类型有请求体
      get虽然没有请求体,但是可以将需要向服务端传递的数据表现在url地址的请求字符串中
      http://localhost/index.html?name=admin&pass=123456

      ?参数名=参数值&参数名=参数值这种格式叫做请求字符串/查询字符串,是附加在请求的url的后面
      使用url模块来解析url中的字符串转变为对象的形式

      const url = require(‘url’)
      url.parse(request.url,true); //{name:admin,pass:123456}

    • post请求有请求体
      通过事件

      	let s = ''  
      	request.on('data',chunk=>{
      	 s+=chunk;//chunk 获取请求的数据
      	})
      	
      	request.on('end',()=>{
      	 console.log(s); //name=admin&pass=123456
      	 response.end('xxx')
      })
      

响应报文对象:response

三、响应报文:response

  • 响应行:

    • http协议版本号

    • 响应状态码 200、404(找不到)、500(服务器相关的错误) response.statusCode

    • 响应状态字符串 response.statusMessage
      200 -> ok
      404 -> not found
      500 -> internal server error

      响应状态码和响应状态字符串是一一对应的

  • 响应头:
    response.setHeader('头名','头值')
    response.writeHead(响应状态码,{'头名':'头值'})

  • 空行:

  • 响应体:服务端给客户端一个响应结果
    图片、文字、json格式字符串、网站标签内容(html+css)。。。

    response.end()

    response.write()

    write可以调用多次,输出多次的响应结果,但是最后必须要依赖end方法

    end方法虽然可以书写多次,但是最终只能执行第一个end,因为程序一旦遇到了end,就直接将响应体结果响应会客户端

//引入http模块 url模块
const http = require('http');
const url1 = require('url');
//搭建服务对象
const server = http.createServer( (request,response)=>{//request 请求报文对象  response响应报文对象
  const {method,url,httpVersion,headers} = request;//获取请求类型 请求url地址 请求http协议版本号 请求头

  // 设置响应头 两种方法
  response.setHeader('Content-Type','text/html;charset=utf-8');  
  response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});  
  response.setHeader('a','100');//自定义响应头信息
  if(method === "GET"){
    // http://localhost/index.html?name=admin&pass=123456
    let data = url1.parse(url,true)//data.query属性值从字符串转换成对象{name:admin,pass:123456} 
    console.log(data.query,data.pathname);//pathname 请求的url地址
  }else if(method === "POST"){
    // 通过事件获取post请求体
    let str = '';
    //接收请求数据中
    request.on('data',chunk=>{
      str += chunk;//请求数据
    })
    //接收数据完成
    request.on('end',()=>{
      response.end(str);//设置响应体
    })
  }
  
  response.end('设置内容');//设置响应体方法一
  //设置响应体方法二
  // response.write('内容')
  // response.write('内容1')
  // response.write('内容2')
  // response.end('设置内容3');
});
//设置监听端口
server.listen(8080,()=>{
  console.log('服务启动成功')
})

HTTP协议

一、概念

HTTP(hypertext transport protocol)协议;中文叫超文本传输协议

是一种基于TCP/IP的应用层通信协议

这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。

协议中主要规定了两个方面的内容

  • 客户端:用来向服务器发送数据,可以被称之为请求报文
  • 服务端:向客户端返回数据,可以被称之为响应报文

报文:其实就是一堆字符串

二、请求报文的组成 【重点】

  • 请求行
  • 请求头
  • 空行
  • 请求体

三、HTTP的请求行内容

  • 请求类型(get、post、put、delete等)

  • 请求URL(统一资源定位器)

    例如:http://www.baidu.com:80/index.html?a=100&b=200#logo

    • http: 协议(https、ftp、ssh等)
    • www.baidu.com 域名
    • 80 端口号
    • /index.html 路径
    • a=100&b=200 查询字符串(请求字符串)
    • #logo 哈希(锚点链接)
  • HTTP协议版本号

四、HTTP请求头结构以及常见请求头

格式:头名:头值;

常见的请求头有:

请求头可以程序员自定义!!!

请求头解释
Host主机名
Connection连接的设置 keep-alive(保持连接);close(关闭连接)
Cache-Control缓存控制 max-age = 0 (没有缓存)
Upgrade-Insecure-Requests将网页中的http请求转化为https请求(很少用)老网站升级
User-Agent用户代理,客户端字符串标识,服务器可以通过这个标识来识别这个请求来自哪个客户端 ,一般在PC端和手机端的区分
Accept设置浏览器接收的数据类型
Accept-Encoding设置接收的压缩方式
Accept-Language设置接收的语言 q=0.7 为喜好系数,满分为1
Cookie后面单独讲

五、HTTP的请求体

请求体内容的格式是非常灵活的,

(可以是空)==>GET请求,

(也可以是字符串,还可以是JSON)===》POST请求

例如:

  • 请求字符串(参数名=参数值&参数名=参数值):keywords=手机&price=2000
  • JSON(键名必须是双引号,键值如果是字符串也必须是双引号):{“keywords”:“手机”,“price”:2000}

六、响应报文的组成以及响应行

  • 响应行

    HTTP/1.1 200 OK
    
    • HTTP/1.1:HTTP协议版本号

    • 200:响应状态码 404 Not Found 500 Internal Server Error

      还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

    • OK:响应状态字符串

    响应状态码和响应字符串关系是一一对应的。

  • 响应头

  Cache-Control:缓存控制 private 私有的,只允许客户端缓存数据
  Connection 链接设置
  Content-Type:text/html;charset=utf-8  设置响应体的数据类型以及字符集,响应体为html,字符集utf-8
Content-Length:响应体的长度,单位为字节
  • 空行

  • 响应体

​ 响应体内容的类型是非常灵活的,常见的类型有HTML、css、js、图片、JSON、视频、音频、font…

七、搭建WEB服务

使用nodejs 创建HTTP服务器

三步走:

① 引入http内置模块

const http = require("http")

② 创建服务

const server = http.createServer((request,response)=>{  //普通函数或箭头函数均可
	//根据请求,设置响应报文内容
	//end方法可以设置响应体
	response.end("xxx")
})
  • request:是一个对象,可以获取请求报文中的数据
  • response:是一个对象,可以设置响应报文中的数据

③ 监听端口

  • 端口号:
  • 是计算机的服务窗口,总共是65536个服务窗口,程序可以监听端口来处理指定端口的数据
  • 常见端口号的选择:8000、8080、8888、3000、9000 默认端口号为80,在『浏览器请求时』可以不写

其他端口号尽量选择大于1024的

server.listen(端口号,回调函数)

④ 浏览器请求对应端口

http://127.0.0.1:端口号
  • 127.0.0.1始终都是指向本机,IP地址是计算机在网络中唯一的门牌号,

  • IP地址一般由四位数字组成,ipv4(第4个版本)

  • 获取某网站的ip地址:

ping 网站域名

常见问题:

(1)当服务已经启动后

  • 想要修改JS代码,刷新浏览器后,发现页面内容无变化
答:JS代码修改之后,『必须重启』服务(并非重新运行该文件),才能看到最新的显示结果
  • 端口号被占用

Error: listen EADDRINUSE: address already in use :::8000

解决办法1:停止当前正在运行监听端口的服务 【使用较多】

答:找到对应命令行窗口,使用『ctrl+c』组合键

解决办法2:修改其他端口号使用

  • 响应内容中文,浏览器中显示为乱码现象
答:response.writeHead(200,{"Content-Type":"text/plain;charset=utf-8"})  #默认为text文本
//1.引入http模块
const http = require('http');

//2.创建服务
const server = http.createServer((request,response)=>{
    response.end('hello http');
})
//3.监听端口
server.listen(80,()=>{
    console.log('80端口服务已启动');
})

八、获取HTTP请求的数据

想要获取请求的数据,必须通过request对象;

其中包含以下参数:

参数名含义语法重点掌握
method请求方法request.method*****
httpVersion请求版本request.httpVersion
url请求路径request.url*****
headers请求头request.headers*****
on请求体request.on(‘data’, function(chunk){})
request.on(‘end’, function(){});

特殊说明:

1、request.url:只能获取路径以及查询字符串,无法获取url中的域名以及协议的内容;

2、request.headers:将请求信息转化成一个对象,并将属性名都转化成了『小写』

3、关于路径:如果访问网站的时候,只填写了IP地址或者是域名信息,此时请求的路径为『/』

4、关于ip:ip是计算机在网络中的唯一门牌号,ipv4 156.23.56.85

​ 127.0.0.1 始终是指向本机的

4、关于 favicon.ico:这个请求是属于『谷歌浏览器』自动发送的请求,其他浏览器就不一定会发送了,

 用来获取当前网站的『小图标』

练习1:按照以下要求,搭建http服务,并按照不同url地址访问

请求类型请求地址响应体结果
get/login登录页面
get/register注册页面
//1、引入http模块
const http = require("http");
//2、建立服务
const server = http.createServer((request,response)=>{
    let {url,method} = request; //对象的解构赋值
    //设置响应头信息
    //解决中文乱码
    response.setHeader("Content-Type","text/html;charset=utf-8")
    if(url == "/register" && method == "GET"){
        response.end("注册页面");
    }else if(url=="/login" && method == "GET"){
        response.end("登录页面");
    }else{
        response.end("<h1>404 Not Found</h1>")
    }
})

//3、监听端口
server.listen(8000,()=>{
   console.log('服务启动中....');
})

当请求url中携带请求参数时,就需要借助于url模块了

① 引入http内置模块

let http = require("http")

② 引入url模块

let url = require("url");

③ 创建服务

let server = http.createServer((request,response)=>{
	//针对于请求中的url进行解析
    //第二个true表示将query属性值从字符串转换成对象
	//语法:url.parse(request.url,true)
	response.end("xxx")
})

url模块中常用的参数

  • pathname:url中的路径部分

  • query:url中的查询字符串对象

③ 监听端口

端口号:

  • 是计算机的服务窗口,总共是65536个服务窗口,程序可以监听端口来处理指定端口的数据
  • 常见端口号的选择:8000、8080、8888、3000、9000 默认端口号为80,在『浏览器请求时』可以不写
  • 其他端口号尽量选择大于1024的

练习2:按照以下要求,搭建http服务,并按照不同url地址访问,修改页面的背景颜色

例如:

/server?bg=yellowgreen

/server?bg=red

//1、引人http模块
const http = require("http");
//2、引入url模块
const url = require("url");
//3、创建服务
const server = http.createServer((request,response)=>{
    // console.log(url); //url是一个对象
    //如果想要解析地址栏的请求地址,需要调用url对象中的parse方法
    //url.parse的第二个参数为true时将使用查询模块来分析查询字符串,默认为false,所以咱们设置为true
    let data = url.parse(request.url,true);
    let bg = data.query.bg?data.query.bg:'pink';
    console.log(bg);
    //设置响应结果的显示颜色
    response.end(`<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            body{
                background:${bg}
            }
        </style>
    </head>
    <body>
    </body>
    </html>`)
})
//4、监听端口
server.listen(8000,()=>{
    console.log('服务正在启动中....ing');
})

九、获取HTTP请求体的数据

当请求的数据来自于表单,那么在响应服务之前,就需求获取请求体中的数据

<form method="post" action="http://127.0.0.1/">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="text" name="userpass"/><br/>
    <button>登录</button>
</form>

关于请求体:

  • GET请求的请求体绝大多数为空
  • POST请求的请求体大多数都不为空

请求体获取的三步走:

1、声明一个字符串,用来显示请求信息

let  str = "";

2、设置绑定事件,利用可读流数据读取的事件

request.on('data',chunk=>{
	str+=chunk;
})

3、绑定事件,当数据读取完毕时触发的事件

request.on('end',()=>{
	//获取请求体结果
	console.log(str);
	//响应结果
	response.end("over~~~")
)
//1、引入http模块
const http = require("http");
//2、创建服务
const server = http.createServer((request,response)=>{
    //4、声明一个字符串
    let str = "";
    //5、绑定事件
    request.on('data',chunk=>{
        str+=chunk;
    })
    request.on('end',()=>{
        //获取请求体结果
        console.log(str);
        //响应结果
        response.end('over~~~')
    })
})
//3、监听端口
server.listen(8000,()=>{	
    console.log('服务启动中.....');
})

十、HTTP模块响应相关的API

  • response.statusCode 设置响应状态码
  • response.statusMessage 设置响应状态字符串 (设置的较少
  • response.setHeader(‘头名’,‘头值’) 自定义头信息
  • response.write(‘xx’) 设置响应体
  • response.end(‘xxx’) 设置响应体
write和end的两种使用情况:
//1.情况1: write和end的结合使用  响应体相对分散
response.write('xx');
response.write('xx');
response.write('xx');
...
response.end(); //每一个请求,在处理的时候必须要有 end 方法的执行,不能省略

//2.情况2:单独使用end方法  响应体相对集中
response.end('xxx');

案例:搭建HTTP服务,响应一个表格4行3列,并实现隔行换色(JS)

//1.引入http模块
const http = require('http');

//2.创建服务
const server = http.createServer((request,response)=>{
    response.end(`
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Document</title>
                <style>
                   td{
                       width:100px;
                       height:50px;
                   }
                   table,td{
                       border-collapse:collaspe;
                   }
                </style>
            </head>
            <body>
                <table border="1" align="center" cellpadding="3" cellspacing="3">
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td></td>
                        <td></td>
                    </tr>
                </table>
                <script>
                   //获取所有的tr行
                   let trs = document.querySelectorAll('tr');
                   //循环
                   trs.forEach((item,index)=>{
                        if(index%2==0){
                            item.style.background = "orange";
                        }
                        else{
                            item.style.background = "green";
                        }
                   })
                </script>
            </body>
            </html>
    `)
})

//3.监听端口
server.listen(80,()=>{
    console.log('80端口启动中....ing');
});

案例2:搭建服务,响应带数据的表格

const data = [
    {
        id:1,
        name:'孙燕姿',
        song:'逆光'
    },
    {
        id:2,
        name:'周杰伦',
        song:'不能说的密码'
    },
    {
        id:3,
        name:'林俊杰',
        song:'不为谁而做的歌'
    },
    {
        id:4,
        name: '五月天',
        song:'干杯'
    },
    {
        id: 5,
        name: '张艺兴',
        song: '莲'
    },
    {
        id:6,
        name:'刘德华',
        song:'冰雨'
    },
    {
        id: 7,
        name: '张学友',
        song: '情人'
    }
]

//要求:搭建表格,数据来自于data变量
require('http').createServer((request,response)=>{
    let body = "";
    body+=`
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Document</title>
                <style>
                   td{
                       width:100px;
                       height:50px;
                   }
                   table,td{
                       border-collapse:collaspe;
                   }
                </style>
            </head>
            <body>
                <table border="1">
                            <tr>
                                <td>id</td>
                                <td>歌手名称</td>
                                <td>成名曲</td>
                            </tr>`;
            data.forEach(item=>{
                body+=`
                    <tr>
                        <td>${item.id}</td>
                        <td>${item.name}</td>
                        <td>${item.song}</td>
                    </tr>
                    `
            })

            body+=`
                </table>
            </body>
            </html>
    `;
    response.end(body);
}).listen(80,()=>{
    console.log('80端口正在运行中....ing');
})

关于响应:

  • 响应文件内容:对于HTML内容,长时间不发生改变的文件,使用该方式
  • 响应拼接后的结果:对于网页内容,经常发生改变的文件,使用该方式
  • 而在工作中,最常见的情况是服务器端响应JSON

十一、响应文件内容固定实现

require('http').createServer((request,response)=>{
    //获取请求的方法已经路径
    let {url,method} = request;
    //判断请求方式以及请求路径
    if(method == "GET" && url == "/index.html"){
        //需要响应文件中的内容
        let data = require('fs').readFileSync(__dirname + '/index.html');
        response.end(data);
    }else if(method == "GET" && url == "/css/app.css"){
        //需要响应文件中的内容
        let data = require('fs').readFileSync(__dirname + '/public/css/app.css');
        response.end(data);
    }else if(method == "GET" && url == "/js/app.js"){
        //需要响应文件中的内容
        let data = require('fs').readFileSync(__dirname + '/public/js/app.js');
        response.end(data);
    }
    else{
        //404响应
        response.statusCode = 404;
        response.end("<h1>404 Not Found</h1>");
    }
}).listen(80,()=>{
    console.log('80端口正在启动中....');
})

很明显上面的代码,当只要有一个请求路径就需要进行判断,显然这种方式不够完美,那么我们需要封装

require('http').createServer((request,response)=>{
    //获取请求的方法已经路径
    let {url,method} = request;
    //文件夹路径
    let rootDir = __dirname + '/public';
    //拼接文件路径
    let filePath = rootDir + url;
    //读取文件内容
    fs.readFile(filePath,(err,data)=>{
        //判断
        if(err){
            //如果出现错误,响应404状态码
            response.statusCode = 404;
            response.end('<h1>404 Not Found</h1>');
        }else{
            //响应文件内容
            response.end(data);	
        }
    })
}).listen(80,()=>{
    console.log('80端口正在启动中....');
})

十二、GET和POST请求的区别

GET和POST是HTTP协议请求的两种方式。

  • GET主要用来获取数据,POST主要用来提交数据

  • GET带参数请求是将参数缀到URL之后,在地址栏中输入url访问网站就是GET请求,

    POST带参数请求是将参数放到请求体中

  • POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏

  • GET请求大小有限制,一般为2K,而POST请求则没有大小限制

  • GET类型报文请求方法的为GET,POST类型报文请求方法为POST

情况1:针对于安全性来说

​ POST更安全,GET安全系数较低

情况2:表现形式

​ POST提交数据通过请求体来提交

​ GET是将请求的数据暴露在地址栏中的请求字符串里面

情况3:传递数据大小

​ GET基本传递数据不到2KB

​ POST大小基本不上限(但是可以通过后台代码(java、php…)来设置)

情况4:使用场景

​ GET通常用于获取数据

​ POST通常用于提交数据

十三、GET和POST使用场景

GET请求的情况:

  • 在地址栏直接输入url访问
  • 点击a链接
  • link标签引入css
  • script标签引入js
  • img标签引入图片
  • form标签中的method为get
  • ajax中的get请求

post请求的情况:

  • form标签中的method为post
  • ajax的post请求
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值