使用Nodejs创建HTTP服务
创建HTTP服务初体验
//1. 引入HTTP模块
const http = require('http');
//2. 调用方法 创建服务对象
/**
* request 请求报文的封装对象
* response 响应报文的封装对象
*/
const server = http.createServer(function(request,response){
/*
此方法向服务器发出信号,表明所有响应头和正文都已发送;该服务器应认为此消息已完成。 response.end() 方法必须在每个响应上调用。
如果指定了 data,则其效果类似于调用 response.write(data, encoding) 后跟 response.end(callback)。
如果指定了 callback,则将在响应流完成时调用。
*/
response.end('Hello HTTP server');
});
//3. 监听端口
/**
* 端口号: 计算机的服务窗口,一共有65536个窗口,也就是2^16
* 默认端口:80
* 127.0.0.1 这是本机的地址
*/
server.listen(8000,function(){
console.log("服务已经启动,端口8000监听中....");
})
局域网和云服务器上的配置
局域网
使用ipconfig
查到自己的ip地址,使用ip地址加端口号一样能访问到本机开启的服务,在同一个局域网的主机能同样能访问到。
云服务器
获得请求报文中的参数
在请求的参数中包括行,头,体。
请求行和请求头
const http = require('http');
const url = require('url');//已被弃用, 使用URL类
const server = http.createServer(function (request, response) {
// 获取报文中的内容
// 行中有 请求的方式 url HTTP/1.1
// 1. 请求的类型
console.log(request.method);
//2. 请求的url / 也就是url的路径内容和查询字符串
// 也就是 /pathname?quertstring
console.log(request.url);
// 提取url里面的路径
console.log(url.parse(request.url));
console.log(url.parse(request.url).pathname);// 获取路径
console.log(url.parse(request.url).search);// 获取路径加查询字符串
console.log(url.parse(request.url).query);// 获取查询字符串
console.log(url.parse(request.url,true).query);// 把查询字符串转为对象,这样皆可以获取参数了
//3 协议的版本
console.log(request.httpVersion);
console.log('Hello');
response.end(); // 这个必须要加入,不要无法显网页,也就是客户端没有得到回应
// 请求头信息的获取
console.log(request.headers);
})
server.listen(80, function () {
console.log('服务器已启动, 端口80 监听中');
})
请求体
只要POST方法才有请求体,那么这里就创建一个form表单来测试。
<!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>
<body>
<form action="http://127.0.0.1" method="post">
用户名<input type="text" name="username" id=""><br>
密码<input type="password" name="password" id="">
<button>登录</button>
<!-- button 和 submit都可以 -->
</form>
</body>
</html>
const http = require('http');
const querystring = require('querystring');// 解析请求字符串
const server = http.createServer(function (request, response) {
// get请求,请求体是空的,只有post有请求体。
//1. 声明字符串
let body = '';
//2. 绑定 data 事件
request.on('data',(chunk) => {// 每读取一次数据,执行一次回调
// 拼接
body += chunk.toString();
});
//3. 绑定 end 事件,请求体数据读取完毕
request.on('end',()=>{
console.log(body);
console.log(querystring.parse(body));
response.end('get it ');// 应该放在这个,要不然他是同步流,还没读完就响应了
})
})
server.listen(80, function () {
console.log('服务器已启动, 端口80 监听中');
})
响应结果设置
code
响应头
对于响应头,我们可以做设置状态码和状态字符创
- 状态码
response.statusCode = 404; // 设置状态码
-
响应字符串
response.statusMessage = 'haha';
响应头
使用`response.setHeader()`
response.setHeader('Content-type','text/html;charset=utf-8');
response.setHeader('abc','1000');
响应体
响应体不能为空,否则无法响应成功
响应体不能为空,如果没有write写入,而且end里面没有内容,那么将无法响应。
response.end()
和 response.write()
都可以写入响应体
response.write('111');
response.write('22');
response.write('33');
response.write('132');
response.end();// 响应体不能为空,如果没有write写入,而且end里面没有内容,那么将无法响应
code
const http = require('http');
http.createServer((request, response) => {
// 响应行
/*
响应行分为:协议版本 状态码 状态字符串
*/
// 状态码
response.statusCode = 404;
// 响应状态码
response.statusMessage = 'haha';
// 响应头
response.setHeader('Content-type','text/html;charset=utf-8');
response.setHeader('abc','1000');
// 响应体
response.write('111');
response.write('22');
response.write('33');
response.write('132');
response.end();// 响应体不能为空,如果没有write写入,而且end里面没有内容,那么将无法响应
}).listen(80)
案例–背景变色
在查询字符中/?bg=rgb(x,x,x) 可以更换背景颜色
const http = require('http');
const url = require('url');
const server = http.createServer((request, response) => {
// 中文乱码,必须设置响应头,或者是在用<meta charset = 'UTF-8'>设置,但是响应头的优先级更高
response.setHeader('Content-Type','text/html;charset=utf-8');
// 获取url中的bg参数
let bg = url.parse(request.url,true).query.bg ? url.parse(request.url,true).query.bg : 'pink';
response.end(`
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<style>
body {
background: ${bg};
}
</style>
</head>
<body>
<h1>
红红火火恍恍惚惚或或或或或或或
</h1>
</body>
</html>
`);
});
server.listen(80, () => {
console.log('server has started');
})
注意点:
- 中文必须在请求头设置字符集
response.setHeader('Content-Type','text/html;charset=utf-8');
,或者在用<meta charset = 'UTF-8'>
设置,但是响应头的优先级更高。 - 使用模板字符串超级方便。
案例–根据路径返回页面
get /login 返回登录页面
get /register 返回注册页面
post /register 保存注册结果(用户名和密码)
nodejs代码:
const http = require('http');
const url = require('url');
const fs = require('fs');
const qs = require('querystring');
const server = http.createServer((request, response) => {
// 中文乱码,必须设置响应头,或者是在用<meta charset = 'UTF-8'>设置,但是响应头的优先级更高
response.setHeader('Content-Type','text/html;charset=utf-8');
let method = request.method;
let pathname = url.parse(request.url,true).pathname;
if(method.toUpperCase() === 'GET' && pathname === '/login'){
response.end(fs.readFileSync(__dirname + '/login.html'));// 同步读入
}else if(method.toUpperCase() === 'GET' && pathname === '/register'){
response.end(fs.readFileSync(__dirname + '/register.html'));// 同步读入
}else if(method.toUpperCase() === 'POST' && pathname === '/register'){
// 提取用户注册的信息
let body = '';
request.on('data',chunk=>{
// chunk 数据块
body+=chunk;
})
// 读取结束
request.on('end',()=>{
// 解析用户数据为对象
const data = qs.parse(body);
let temp = fs.readFileSync('./user.json').toString();// 这次读取出来的是Buffer
temp = JSON.parse(temp);// 解析为对象
temp.data.push(data);// 向对象里面的数组压入数据
fs.writeFileSync('./user.json',JSON.stringify(temp));
console.log(`用户提交的内容为${body}`);
})
//console.log(body);// 这里是同步的,所以body是空字符串
response.end('注册成功');
}else{
response.end('404 Not found');
}
});
server.listen(80, () => {
//console.log(__dirname);// 这个可以打印当前文件的绝对路径
console.log('server has started');
})
user.json
{
"data": [{
"username": "124124",
"pws": "123123"
}]
}
register.html
如果表单的action
不加协议头和主机名,那么它会使用当前的url。
<!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>注册界面</title>
<style>
form{
height: 500px;
width: 500px;
margin: 100px auto;
}
</style>
</head>
<body>
<form action="http://127.0.0.1/register" method="post">
<div class="container">
<input type="text" name='username'>
<br>
<input type="password" name='pws'>
<br>
<input type="submit" value="注册">
</div>
</form>
</body>
</html>
服务端使用<script>标签引入js代码
html中的代码link,script ,img
这种可以请求外部文件的标签也可以发送请求。只需要对路径进行一些设置即可。
不过关需要注意的是,如果加了/
那么前面就需要路径,不管是.
还是什么。但是若前面不加/
那么就意味着当前路径。
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>
<body>
<!-- 这里完全可以写 /引入JavaScript代码.js-->
<script src="http://127.0.0.1/引入JavaScript代码.js"></script>
</body>
</html>
引入的js代码
let body = document.body;
body.innerHTML = '引入成功';
nodejs代码
const http = require('http');
const url = require('url');
const fs = require('fs');
const qs = require('querystring');
const server = http.createServer((request, response) => {
response.setHeader('Content-Type','text/html;charset=utf-8');
let pathname = decodeURI(url.parse(request.url,true).pathname);// 解决路径中文问题
console.log(pathname);
if(pathname === '/引入JavaScript代码.js'){
console.log(123);
response.end(fs.readFileSync('./引入JavaScript代码.js'));
}else{
response.end(fs.readFileSync('./引入JavaScript代码.html'));
}
});
server.listen(80, () => {
console.log('server has started');
})
感悟
可以吧url看做服务器开启服务文件夹的目录,在它的后面加上文件的pathname,那么就可以通过服务器开启的服务来判断该响应什么文件给它。
多路径请求
像之前那个写if else判断实在是太麻烦了,而且无法写出所以的情况。
我们在项目下创建一个public文件来存放页面代码
public
index.html css index.css app.css js index.js
定义一个filePath
// 网站的根目录,是网站请求服务的根目录
let dirctory = __dirname + '/public';
let filePath = dirctory + pathname;
访问文件
fs.readFile(filePath,(err,data)=>{
if(err){// 如果没有找到此文件
response.statusCode = 404;
response.end("<h1>404 Not Found</h1>");
}else{
response.end(data);
}
})
服务交互总结
输入url
,开始向服务请求资源,在解析为资源的时候,发现又有请求,又想服务器发送请求,得到需要的数据继续解析。如此往复可得到最终的结果。