node.js的http模块

http模块

http协议:超文本传输协议(互联网应用最广泛的协议之一)


http请求方法:

  • get获取数据
  • post新增数据
  • put/patch更新数据
  • delete删除数据
  • head/options/connect/trace

    URL统一资源定位符,其本身也是一个字符串。

客户端与服务

在网络节点中,负责消费资源的电脑,叫做客户端。
负责对外提供网络资源的电脑,叫做服务器。
http模块是node.js官方提供的、用来创建web服务器的模块。通过http模块提供的http.createServer()方法,就能方便的把一台普通电脑变成一台web服务器,从而对外提供web资源服务。

使用http模块创建web服务器,需要先导入

const http=require('http')

服务器和普通电脑的区别就是服务器上安装了web服务器软件,例如IIS,Apache等,在Node.js中不需要使用第三方web服务器软件,可以基于Node.js提供的http模块,简单代码手写一个服务器软件,对外提供web服务。

服务器相关概念

ip地址

ip地址是互联网上每台计算机的唯一地址,ip地址具有唯一性。

ip地址的格式通常是“点分十进制”,表示成(a,b,c,d)的形式,其中a,b,c,d都是0~255之间的十进制整数。eg:(192.168.1.1)

互联网中的每台web服务器都有自己的ip的地址,可以在Windows终端运行ping www.baidu.com命令,即可查看百度服务器的ip地址。

在这里插入图片描述

域名和域名服务器

ip地址不直观不便于记忆,于是有字符型的地址方案-域名地址

ip地址和域名是一一对应的关系,这种对应关系放在一种叫做域名服务器的电脑中。使用者只需要通过域名访问对应服务器即可,对应的转换工作由域名服务器实现。

因此,域名服务器就是提供ip地址和域名之间的转换服务的服务器。

注意:开发测试期间,127.0.0.1对应的域名是localhost,他们都代表我们自己的这台电脑,在使用效果上没有任何区别。

端口号

一台电脑可以运行成百上千个web服务,每个web服务都对应一个唯一的端口号,客户端发送过来的网络请求,通过端口号,可以被准确的交给对应的web服务进行处理。

注意:

每个端口号不能同时被多个web服务占用。

在实际应用中,url中的80端口可以被省略。

localhost:80可以直接访问localhost

网页中URL的使用

URL可以分为相对路径和绝对路径

相对路径:相对于当前文件位置的路径

绝对路径:目标文件在硬盘上的真实路径(最精确路径)

html中link引入文件的最好的一种方式应该是绝对路径

 <link rel="stylesheet" href="/index.css">

绝对路径

绝对路径靠谱性强,相对容易理解在项目中运用比较多

https://xxx.com/web直接向目标资源发送请求,容易理解。网站的外链会用此形式

//xx.com/web与页面URL的协议拼接行测好难过完整的URL再发送请求。大型网站使用较多。
/web与页面的协议主机名,端口拼接形成完整的URL再发送请求。中小型网站。

相对路径

比如http://www.xxx.com/about/h5.html

相对路径是在发送请求时,与当前页面URL路径进行计算,得到完整的URL后,再发送请求,学习阶段用的较多。

网页中URL使用场景总结

包括但不限于以下场景

  • a标签href
  • link标签href
  • script标签href
  • img标签src
  • video audio标签的src
  • form中的action
  • AJAX请求中的URL

浏览器查看HTTP报文

可以使用一个表单提交的demo做相关测试

<form action="http://127.0.0.1:9000" method="post">
        <input type="text" name="username">
        <input type="text" name="password">
        <input type="submit" value="提交">
    </form>

在浏览器打开对应的html文件(注意在此之前运行http服务端,监听端口开启服务),输入表单信息提交,在F12控制台可以查看网络
在这里插入图片描述

获取请求行和请求头

想要获取请求的数据,需要通过request对象

  • request.method请求方法
  • request.httpVersion请求版本
  • request.url请求路径
  • require(‘url’).parse(request.url).pathname URL路径
  • require(‘url’).parse(request.url,true).query url查询字符串
  • request.headers请求头
  • request.on(‘data’,function(chunk){});request.on(‘end’,function(){});

注意事项

  • request.url只能获取路径以及查询字符串,无法获取URL中的域名以及协议的内容
  • request.headers将请求信息转化为一个对象,并将属性名都转化成了小写。
  • 关于路径:如果访问网站的时候,只填写可ip地址或者域名信息,此时的请求路径是/
  • favicon.ico:浏览器自动发送的请求

创建最基本的web服务器

基本步骤

  1. 导入http模块
const http=require('http')
  1. 创建web服务器实例
    调用http.createServer()方法快速创建一个web服务器实例
    const server=http.createServer()
  1. 为服务器实例绑定request事件,监听客户端请求
    // 使用服务器实例的.on()方法,为服务器绑定一个request事件
    server.on('request', (req, res) => {
        // 只要有客户端来请求我们自己的服务器,就会触发request事件,从而调用这个事件处理函数
        console.log('Hello HTTP Server')
    })

req是一个对请求报文的封装对象,可以获取请求报文的相关内容
res是一个对响应报文的封装对象,可以获取响应报文的相关内容

  1. 启动服务器
    调用服务器的.listen()方法,即可启动当前的web服务器实例
// 调用服务器实例的.listen(端口号,cb回调)方法,即可启动当前的web服务器实例
server.listen(9000, () => {
    console.log('服务已经启动')
})

可以在powdershell执行node命令,
vscode的终端新建终端也可以执行node命令,ctrl+c暂停服务器
在这里插入图片描述

注意事项

  • 命令行ctrl+c停止服务
  • 当服务启动后,更新代码必须重启服务才能生效
  • 响应内容中文乱码的解决办法
response.setHeader('Content-type', 'text/html;charset=utf-8');
  • 端口号被占用
    Error: listen EADDRINUSE: address already in use :::9000
    (1)关闭当前正在运行监听端口的服务
    (2)修改其他端口号

  • HTTP协议默认端口是80,HTTP服务开发常用端口有3000,8080,8090,9000等
    如果端口被其他程序占用,可以使用资源监视器找到占用端口的程序,然后使用任务管理器关闭对应的程序。
    桌面底部任务栏右键任务管理器,点击性能,窗口底部就有资源监视器
    在这里插入图片描述
    在这里插入图片描述

    根据资源监视器找到占用端口的进程的pid,在资源管理器根据pid找到进程右键结束任务,端口就可以用了。另外如果pid没有显示pid,在名称那里右键选择pid就显示了如图
    在这里插入图片描述

  • HTTPS协议默认端口是443

req请求对象

只要服务器接收到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数。

如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下方法:

server.on('request', (req) => {
    //req是请求对象,它包含了与客户端相关的数据和属性,例如:
    // req.url是客户端请求的URL地址
    // req.method是客户端的method请求类型
    const str = 'Your request url is ${req.url}, and request method is ${req.method}'
    console.log(str)
})

运行的完整的代码如下

const http = require('http')

const server = http.createServer()

server.on('request', (req) => {
    //req是请求对象,它包含了与客户端相关的数据和属性,例如:
    // req.url是客户端请求的URL地址
    // req.method是客户端的method请求类型
    const str = 'Your request url is ${req.url}, and request method is ${req.method}'
    console.log(str)
})

// 调用服务器实例的.listen(端口号,cb回调)方法,即可启动当前的web服务器实例
server.listen(1314, () => {
    console.log('http server running at http://127.0.0.1')
})

运行80端口报错
在这里插入图片描述

所以换成了1314端口
运行成功在这里插入图片描述

res响应对象

在服务器的request事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下方式:

server.on('request', (req, res) => {
    // res是响应对象,它包含了与服务器相关的数据和属性,例如 要发送到客户端的字符串
    const str = 'Your request url is ${req.url}, and request method is ${req.method}'
    // res.end()方法向客户端发送指定内容,并结束这次请求的处理过程
    res.end(str)
})

完整运行的代码

// 导入http模块
const http = require('http')
// 创建web服务器实例
const server = http.createServer()
// req是请求对象,包含与客户端相关的数据和属性
server.on('request', (req,res) => {
    // req.url事故客户端请求的url地址
    const url = req.url
    // req.method是客户端请求的method类型
    const method = req.method
    const str = 'Your request url is ${url}, and request method is ${method}'
    console.log(str)

    // 调用res.end()方法,向客户端响应一些内容
    res.end(str)
})
server.listen(1314, () => {
    console.log('server running at http://127.0.0.1')
})

运行结果截图
在这里插入图片描述

创建HTTP报文和提取请求体的代码示例

const http = require('http');
// 创建服务对象
const server = http.createServer((request, response) => {
// 获取请求的方法
    console.log(request.method);
    // 获取请求的url
    console.log(request.url);//只包含url中的路径与查询字符串
    // 获取HTTP协议的版本号
    console.log(request.httpVersion)
    // 获取HTTP的请求头
    console.log(request.headers.host);
    response.end('http');//设置响应体
});
// 监听端口,启动服务
server.listen(9000, () => {
    console.log('服务已经启动')
});

在地址栏输入请求
在这里插入图片描述

request.url会在终端输出url路径与查询字符串
在这里插入图片描述

提取HTTP报文的请求体

const http = require('http');
// 创建服务对象
const server = http.createServer((request, response) => {
// 声明一个变量
    let body = '';
    // 绑定data事件
    request.on('data', chunk => {
        body += chunk;
    })
    // 绑定end事件
    request.on('end', () => {
        console.log(body);
        // 响应
        response.end('Hello HTTP');
    });
});
// 监听端口,启动服务
server.listen(9000, () => {
    console.log('服务已经启动')
});

在这里插入图片描述

在地址栏直接发送请求是get请求,请求体为空,服务端返回空
在这里插入图片描述

可以借助html的表单发送不为空的请求

提取HTTP报文中URL的路径与查询字符串

const http = require('http');
const url = require('url');
const server = http.createServer((request, response) => {
    let res = url.parse(request.url, true);
    console.log(res);
    let pathname = res.pathname;
    let keyword = res.query.keyword;
    console.log(keyword);
    response.end('url');
});
// 监听端口,启动服务
server.listen(9000, () => {
    console.log('服务已经启动')
});

一开始没有监听端口启动服务,我还想怎么node命令之后没有任何反应呢,搞笑。。。
在这里插入图片描述

还有一种方式,代码如下

const http = require('http');
const { URL } = require('url');
const server = http.createServer((request, response) => {
    // 实例化url的对象
    // let url = new URL('http://www.xxx.com/search?a=100&b=200');
    // let url = new URL('./search?a=100&b=200', 'http://127.0.0.1:9000');
        let url = new URL(request.url, 'http://127.0.0.1:9000');
//    输出路径
    console.log(url.pathname);
    // 输出keycode查询字符串
    console.log(url.searchParams.get('keyword'));
    response.end('url new');
});
server.listen(9000, () => {
    console.log('服务已经启动')
});

注意实例化url对象的代码。

HTTP请求练习题

按照以下要求搭建HTTP服务
请求类型get,请求地址为/login和/reg响应体结果分别是登录页面和注册页面。

核心步骤

  • 获取请求的url地址
  • 设置默认的相应内容为404 Not found
  • 判断用户请求的是否为/login.html
  • 判断用户请求的是是否是/reg.html
  • 设置Content-Type响应头,防止中文乱码
  • 使用res.end()把内容响应给客户端

基础的代码模板如下

// 1.导入http模块
const http = require('http');
// 2.创建服务对象
const server = http.createServer((request, Response) => {
    Response.end('practise');
});
// 3.监听端口,启动服务
server.listen(9000, () => {
    console.log('服务已经启动,,端口9000监听中')
})

上述代码为基础的代码模板,因为有请求就会执行回调函数,所以功能函数会放在回调函数当中,并且需要在当中判断请求方法以及是否url路径是否与要求匹配。
由于最后只能有一个end方法,所以在写完判断的end的方法之后要把原来的practise的end方法删除掉。否则会出现write after end 的报错信息。
删除后运行页面的内容出现中文乱码,这个时候只需要设置response.setHeader即可。

let method=request.method;
可以简写为
let { method } = request;
request里面有method属性,所以可以用赋值的方式去获取。[对象解构]

完整代码如下,最后的else保证了谷歌浏览器favicon.ico的响应

// 1.导入http模块
const http = require('http');
// 2.创建服务对象
const server = http.createServer((request, Response) => {
    // 获取请求的方法
    let { method } = request;
    // 获取请求的url路径
    let { pathname } = request;
    // let pathname = new URL(request.url, 'http://127.0.0.1');
    Response.setHeader('content-type', 'text/html;charset=utf-8');
    // console.log(pathname);
    // console.log(method);
    // 判断
    if (method === 'GET' && require('url').parse(request.url).pathname  === '/login') {
        // 登录的情形
        Response.end('登录页面');
    } else if (method === 'GET' && require('url').parse(request.url).pathname  === '/reg') {
        Response.end('注册页面');
    } else {
        Response.end('404 Not Found');
    }
    
});
// 3.监听端口,启动服务
server.listen(9000, () => {
    console.log('服务已经启动,,端口9000监听中')
})

【bug记录】

判断的逻辑如下,判断之前打印pathname和method,前者是url集合后者是get,login和reg页面也无法正常切换,页面无论如何只显示404notfound的默认样式。
最后将pathname换成了require(‘url’).parse(request.url).pathname,页面切换正常。或者在上面使用对象解构声明let { pathname } = request;

//存在问题的代码示例
    if (method === 'GET' && pathname  === '/login') {
        // 登录的情形
        Response.end('登录页面');
    } else if (method === 'GET' && pathname  === '/reg') {
        Response.end('注册页面');
    } else {
        Response.end('404 Not Found');
    }

设置HTTP响应报文

  • 设置响应状态码 response.statusCode
  • 设置响应状态描述 response.statusMessage(用的非常少)
  • 设置响应头信息 response.setHeader(‘头名’,‘头值’)
  • 设置响应体 response.write(‘xx’)或者 response.end(‘xxx’)

在服务对象里设置响应状态码
Response.statusCode = 203;
默认的响应状态码是200,多次设置后面设置的会覆盖前面的哈哈哈。

在服务对象里面设置响应状态描述
Response.statusMessage = ‘sorry tony’;

在这里插入图片描述

在服务对象里面设置响应头

  • Response.setHeader(‘content-type’, ‘text/html;charset=utf-8’);
  • Response.setHeader(‘server’, ‘node.js’);
  • // Response.setHeader(‘暗号’, ‘是谁在等天明’);//error
  • Response.setHeader(‘test’, [‘a’, ‘b’, ‘c’]);

    在这里插入图片描述

响应头设置中文内容给会报错
在这里插入图片描述

响应体的设置

  • Response.write(‘love’);
  • Response.write(‘love’);
  • Response.write(‘love’);
  • Response.write(‘love’);
  • Response.end();//设置响应体

响应体可以重复设置,最后会叠加呈现在页面上,一般write设置了载体,end里面就不会设置载体了。
在这里插入图片描述

HTTP响应练习题

搭建HTTP服务,响应一个4列3行的表格,并且要求表格有隔行换色效果,且点击单元格能高亮显示


const http = require('http');
const fs = require('fs');

const server = http.createServer((request, Response) => {
    // 读取文件内容
    let html = fs.readFileSync(__dirname + '/http响应表格.html');
    Response.end(html);//end可以是字符串可以是buffer
});

server.listen(9000, () => {
    console.log('服务已经启动,,端口9000监听中')
})

html部分如下

<!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>
</head>
<style>
    td {
        padding: 20px 40px;

    }

    table tr:nth-child(odd) {
        background: #aef;
    }

    table tr:nth-child(even) {
        background: #fcb;
    }

    table,
    td {
        border-collapse: collapse;
    }
</style>
<script>
    // 获取所有的td
    let tds = document.querySelectorAll('td');
    // 遍历
    tds.forEach(item => {
        item.onclick = function () {
            this.style.background = '#red';
        }
    })
</script>
<body>
    <table border="1">
        <tr>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </table>
</body>
</html>

前几次运行出现的问题是js部分代码执行的功能是点击对应的表格出现光标,而不是高亮显示,暂时不知道问题出在哪里。
第二天,我笑了,script标签里面的代码写进window.οnlοad=function(){}里面就好了呀哈哈哈。

网页资源加载基本过程

html

<head>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
        <h1>测试</h1>
        <hr>
        <img src="./素材/msld1.jpg">
        <script src="./index.js"></script>
</body>

css


body{
    height: 100vh;
    padding: 20px;
    box-sizing: border-box;
    background: linear-gradient(right  bottom,#aef,#c90);
}
hr{
    margin: 20px 0;
    border-bottom: solid 1px rgba(0, 0, 0, 0.363);
}

js

window.onload = function () {
    let img = document.querySelector('img');
    img.onclick = function () {
        alert('点我干嘛')
    }
}

【记录bug】在这里插入图片描述


考虑添加else条件判断返回img在这里插入图片描述
,依旧不可以

移动图片资源到英文文件夹,依然不显示。

最后文件正常显示是在vscode的html文件中右键 open with live server,在端口5500下正常显示。

静态资源服务

静态资源是指内容长时间不发生改变的资源,比如图片视频,脚本文件,字体文件,html文件等等

动态资源是内容经常更新的资源你,比如百度首页,网易首页,京东搜索列表页面等等。

const http = require('http');
const fs = require('fs');

// 创建服务对象
const server = http.createServer((req, res) => {
    // 获取请求url的路径
    let { pathname } = new URL(req.url, 'http://127.0.0.1');
    // 拼接文件路径
    let filePath = __dirname + '/page' + pathname;
    // 读取文件fs异步API
    fs.readFile(filePath, (err, data) => {
        if (err) {
            Response.statusCode = 500;
            Response.end('文件读取失败');
            return;//防止代码继续往后执行
        }
        // 响应文件内容
        Response.end(data);//设置响应体
    })
});
server.listen(9000, () => {
    console.log('服务已经启动')
});

静态资源目录与网站根目录

http服务在哪个文件夹中寻找静态资源,那个文件夹就是静态资源目录,也称之为网站根目录

vscode中使用live-server访问HTML时,它启动的服务网站根目录是谁

vscode 当前打开的文件夹

const http = require('http');
const fs = require('fs');

// 创建服务对象
const server = http.createServer((req, res) => {
    // 获取请求url的路径
    let { pathname } = new URL(req.url, 'http://127.0.0.1');
    // 声明一个变量
    let root = __dirname + '/../';
    // 拼接文件路径
    let filePath = root + pathname;
    // 读取文件fs异步API
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.setHeader('content-type', 'text/html;charset=utf-8');
            res.statusCode = 500;
            res.end('文件读取失败');
            return;//防止代码继续往后执行
        }
        // 响应文件内容
        Response.end(data);//设置响应体
    })
});
server.listen(9000, () => {
    console.log('服务已经启动')
});

运行结果在这里插入图片描述

设置资源类型mime

媒体类型(MIME类型)是一种标准,用来表示文档、文件或者字节流的性质和格式。

HTTP服务可以设置响应头Content-Type来表明响应体的MIME类型,浏览器会根据该类型决定如何处理资源。

常见文件对应的mime类型

  • application/octet-stream
    这是应用程序文件的默认值。未知的应用程序文件,浏览器一般不会自动执行或询问执行。
  • text/plain
    文本文件默认值。即使它意味着未知的文本文件,但浏览器认为是可以直接展示的。
    text/css
    text/html
    text/javascript
    对于未知的资源类型,可以选择application/octet-stream类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的下载效果。
const http = require('http');
const fs = require('fs');
const path = require('path');

// 创建服务对象
const server = http.createServer((req, res) => {
    // 获取请求url的路径
    let { pathname } = new URL(req.url, 'http://127.0.0.1');
    // 声明一个变量
    // let root = __dirname + '/../';
    let root=__dirname
    // 拼接文件路径
    let filePath = root + pathname;
    // 读取文件fs异步API
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.setHeader('content-type', 'text/html;charset=utf-8');
            res.statusCode = 500;
            res.end('文件读取失败');
            return;//防止代码继续往后执行
        }
        // 获取文件的后缀名
        let ext = path.extname(filePath);
        console.log(ext);
        res.setHeader('content-type','xxx')
        // 响应文件内容
        res.end(data);//设置响应体
    })
});
server.listen(9000, () => {
    console.log('服务已经启动')
});

运行结果截图
在这里插入图片描述

在这里插入图片描述

// 声明一个变量
let mimes = {
    html: 'text/html',
    css: 'text/css',
    js: 'text/javascript',
    png: 'image/png',
    jpg:'image/jpeg'
}
let type = mimes[ext];
        if (type) {
            // 匹配到
            res.setHeader('content-type', type);
        } else {
            // 没有匹配到
            res.setHeader('content-type', 'application/octet-stream');
        }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果css资源中有中文(中文注释也算),那么访问具体的文件的时候页面也会出现乱码,html的不会乱码因为meta设置字符是utf-8,当然,响应头的charset优先级更高一些
在这里插入图片描述
在这里插入图片描述

完善错误处理

打开一个不存在的文件报错,完善错误处理

if (err) {
            // 设置字符集
            res.setHeader('content-type',' text/html; charset=utf-8');
               // 判断错误的代号
            switch (err.code) {
                case 'ENOENT':
                    res.statusCode = 404;
                    res.end('<h1>404 Not Found </h1>');
                case 'EPERM':
                           res.statusCode = 403;
                    res.end('<h1>403 Forbidden </h1>');
            }
            return;
        }

第一个err访问一个不存在的文件,第二个err通过手动修改html的读取属性的权限,禁止读取,然后在测试。右键属性安全编辑里面就可以操作。
在这里插入图片描述
在这里插入图片描述

第三个请求方式的错误处理

可以借助form表单进行post请求,打开表单提交表单发送post请求

在这里插入图片描述
第五种其他的错误处理

 	default:
                  res.statusCode=500;
                  res.end('<h1>500 Internal Server Error </h1>');

GET和POST使用场景

get请求的场景

  • 在地址栏直接输入url访问
  • 点击a链接
  • link标签引入css
  • script标签引入js
  • video和audio引入多媒体
  • img标签引入图片
  • form标签中的method为get(不区分大小写)
  • ajax中的get请求
    post请求的场景
  • form标签中的method为post(不区分大小写)
  • AJAX中的post请求

get与post请求的区别

get和post是http协议请求的两种方式,主要有以下几个区别

  1. 作用,get主要用来获取数据,post主要用来提交数据
  2. 参数位置。get带参数请求是将参数缀到URL之后,post带参数是将参数放到请求体中。
  3. 安全性。 post请求相对get安全一些,因为在浏览器中参数会暴露在地址栏中
  4. get请求大小有限制,一般为2k,而post请求则没有大小限制。

猜你喜欢

转载自blog.csdn.net/weixin_55355282/article/details/131136237