NodeJs 服务器

创建服务器

res.end ();里面的内容可以展示在定义的接口网页中。

  • method1:
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 使用核心模块http模块进行创建
* 涉及到的方法有:
* createServer 创建一个Web服务器 内置一个参数 参数是回调函数 可以不传递参数
* listen方法 这个是服务器的listen方法 主要进行监听
*
*/
const http = require('http');
// 创建服务器 使用createServer方法
// 内置一个参数 参数是回调函数 回调内置两个参数
// 第一个参数是请求 第二个参数是响应
// 返回值是服务器对象
let server = http.createServer((req, res) => {
res.end("hello World")
})
// 进行监听 设定浏览器访问的IP和端口
// 使用listen方法 内置三个参数
// 第一个参数是端口号 必选
// 第二个参数是IP地址 注意 IP地址必须合法 必须使用该电脑的IP 默认地址是127.0.0.1 || localhost
// 第三个参数是回调函数 没什么意义
server.listen(3000, "127.0.0.1", () => {
console.log("服务器正在运行...")
})

打开服务器,然后浏览器输入 127.0.0.1:3000 即可看到 hello World

  • method2:(事件驱动方式)
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const http = require('http');

// 使用createServer方法进行创建服务器
// 这里只是创建服务器对象 但是 不传参数 使用事件驱动
let server = http.createServer();
// 监听端口
server.listen(3000,"127.0.0.1",()=>{
console.log("服务器正在运行...");
})
// 使用事件驱动 驱动服务器
// 使用server对象中的on方法进行驱动事件
// 绑定事件 使用server对象中的on方法 内置两个参数
// 第一个参数是事件的名字 固定事件 request
// 第二个参数是回调函数 事件的处理过程
// 回调参数内置两个参数 第一个是请求 第二个参数是响应
server.on("request",(req,res)=>{
res.end("hello World!")
})

打开服务器,然后浏览器输入 127.0.0.1:3000 即可看到 hello World!

响应

给客户端响应的结果数据

在 NodeJS 创建的服务器上 我们可以响应一些内容 怎么把内容响应到浏览器上,涉及到响应的方法,肯定是 response 的方法

  • response.write () 设置响应的内容,可以有多个,这种方式是直接写出数据,但是并没有关闭流;
  • response.end () 响应结束的信号 ,只能有一个,这种方式是写出最后的数据,并且写出后会关闭流;
js
1
2
3
4
5
6
7
8
9
10
11
12
const http = require("http");

const server = http.createServer((req, res) => {
//响应结果
res.write("one");
res.write("two");
res.end("last");
})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

返回状态码

Http 状态码(Http Status Code)是用来表示 Http 响应状态的数字代码;

  • Http 状态码非常多,可以根据不同的情况,给客户端返回不同的状态码;
  • 常见的状态码:
状态码 状态描述 说明
200 OK 客户端请求成功
400 Bad Request 由于客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized 未授权的错误,必须携带请求的身份信息。
403 Forbidden 客户端没有权限访问,被拒接。
404 Not Found 请求的资源不存在,例如,输入了错误的 URL。
500 Internal Server Error 服务器发生不可预期的错误,导致无法完成客户端的请求。
503 Service Unavailable 服务器不可用,可能处理维护或者重载状态,暂时无法访问。

设置状态码

  • res.statusCode=200;
  • res.writeHead(500);
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const http = require("http");

const server = http.createServer((req, res) => {

// 设置状态码
// res.statusCode=404;
res.writeHead(500);
//响应结果
res.write("one");
res.write("two");
res.end("last");
})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

响应头文件

设置响应的 header;

返回头部信息,主要有两种方式:

  • res.setHeader:一次写入一个头部信息;
  • res.writeHead:同时写入 header 和 status;
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const http = require("http");

const server = http.createServer((req, res) => {

// 设置响应的header
// 设置方式一:
res.setHeader("Content-type", "text/plain;charset=utf8");
// 设置方式二: //设置为text/html时,可以解析html标签
res.writeHead(401,{
"Content-Type":"text/html;charset=utf8"
})

//响应结果
res.write("来啦"); //设置header为charset=utf8,中文就不会乱码了;
res.write("<h2>作用</h2>"); // 设置为text/html时,可以解析html标签
res.end("last");
})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

Header 设置 Content-Type 有什么作用呢?

  • 设置为 text/html 时,可以解析 html 标签;
  • 设置 header 为 charset=utf8, 中文就不会乱码了;
  • 等等

demo1: 通过设置 writeHead (),来防止乱码,识别 H5 标签与设置状态码。

js
1
2
3
4
5
6
7
8
9
10
11
12
13
const http = require("http");
let server = http.createServer().listen(3000, "127.0.0.1", () => {
console.log("服务器正在运行中...")
});
server.on("request", (req, res) => {
// 设置响应头信息 设置MIME
res.writeHead(200,{"Content-type":"text/html;charset=utf-8"})
// 编辑响应内容
res.write('<h1>hellow</h1>');
res.write('<h1>世界</h1>');
// 响应结束
res.end("加油")
})

demo2:通过上述可知,可以用 html 标签,但是上述这样写有点乱,进而直接读取外部的 html 文件:

html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
h1 {
background: red
}
p {
background: yellowgreen
}
</style>
</head>

<body>
<h1>this is h1</h1>
<p>this i p </p>
</body>

</html>
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const http = require('http');
const fs = require('fs');
const path = require('path');

let server = http.createServer().listen(3000, "127.0.0.1", () => {
console.log("服务器正在运行");
})

server.on("request", (req, res) => {
res.writeHead(200, {
"Content-type": "text/html"
});
// 上述案例可以得知,可以用html,这里直接引入一个外部的html文件,而不是在这里写
let contents = fs.readFileSync(path.resolve(__dirname, './ce.html'), 'utf-8');
// 直接用end响应到浏览器上,不用write了
res.end(contents);
})

请求

request 对象中封装了客户端给我们服务器传过来的所有信息:

  • 请求路径:req.url
  • 请求方式:req.method
  • 请求头:req.headers
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const http = require("http");

const server = http.createServer((req,res)=>{
/**
* request对象中封装了客户端给我们服务器传过来的所有信息
* 请求路径:req.url
* 请求方式:req.method
* 请求头:req.headers
*/

// 浏览器地址栏访问:http://127.0.0.1:3000/login?username=fwd&password=123
console.log(req.url); // /login?username=fwd&password=123
console.log(req.method); // GET
console.log(req.headers);
res.end("hello server");
})

server.listen(3000,"0.0.0.0",()=>{
console.log("server is running");
})

如何通过不同的请求路由,做出不同响应呢?

js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const http = require("http");

const server = http.createServer((req, res) => {
// 浏览器地址栏访问:http://127.0.0.1:3000/login
console.log(req.url); // /login
// 浏览器地址栏访问:http://127.0.0.1:3000/login?username=fwd&password=123
console.log(req.url); // /login?username=fwd&password=123

res.end("hello server");
})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

上面我们发现:GET 请求的参数会拼接到路径上,导致即使都是”/login” 的路由,我们通过 req.url 获取到的内容也不相同;此时我们需要借助到 url 模块来进行路由的管理;借助 querystring 模块来进行参数的管理;

js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const http = require("http");
const url = require("url");
const qs = require("querystring");

const server = http.createServer((req, res) => {
// 浏览器地址栏访问:http://127.0.0.1:3000/login?username=fwd&password=123
console.log(req.url); // /login?username=fwd&password=123

const reqUrl = url.parse(req.url);
console.log(reqUrl);
/**
* Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?username=fwd&password=123',
query: 'username=fwd&password=123',
pathname: '/login',
path: '/login?username=fwd&password=123',
href: '/login?username=fwd&password=123'
}
*/
const { pathname, query } = url.parse(req.url);

if (pathname === "/login") {
// 我们想将传过来的参数以对象的形式获取;
console.log(qs.parse(query)); // [Object: null prototype] { username: 'fwd', password: '123' }
res.end("欢迎回来!");
} else {
res.end("请注册~");
}

})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

demo:如果路径是 index.html 则将 ce.html 的界面渲染上去;如果是别的路径,则将 notFound.html 的界面渲染上去;

js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const http = require("http");
const fs = require("fs");
const path = require("path");
const url = require("url");

let server = http.createServer().listen(3000, "127.0.0.1", () => {
console.log("服务器正在运行...")
})

server.on("request", (req, res) => {
// 获取我们请求的路径
let reqUrl = url.parse(req.url).pathname;
if (reqUrl == "/favicon.ico") {
return false;
}
if (reqUrl == "/index.html") {
res.writeHead(200, {
"Content-type": "text/html"
});
res.end(thePath("./ce.html"))
} else {
res.writeHead(404, {
"Content-type": "text/html"
});
res.end(thePath("./notFound.html"))
}
})

// 获取绝对路径
function thePath(absolutePath) {
return fs.readFileSync(path.resolve(__dirname, absolutePath), "utf-8");
}

如何获取 post 请求 body 中的数据呢?

  • 监听数据:req.on (“data”,(data)=>{});
  • 监听数据结束:req.on (“end”, () => {});
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const http = require("http");
const url = require("url");
const qs = require("querystring");

const server = http.createServer((req, res) => {
const { pathname } = url.parse(req.url);
if (pathname === "/login") {
if (req.method == "POST") {
// 获取body中的参数(固定写法)
req.on("data", (data) => {
console.log(data.toString()); //获取到的是个buffer,所以需要toString();
/**
* {
"username":"fwd",
"password":123456
}
*/

//但是获取到的是个字符串,需要转成对象;
const { username, password } = JSON.parse(data.toString());
console.log(username, password); // fwd 123456
})
}
}
res.end("hello world");
})

server.listen(3000, "0.0.0.0", () => {
console.log("server is running");
})

headers 属性

通过 req.headers 获取到的 headers 对象属性;

在 request 对象的 header 中也包含很多有用的信息,客户端会默认传递过来一些信息:

js
1
2
3
4
5
6
7
8
9
10
11
{
'content-type': 'application/json',
'user-agent': 'PostmanRuntime/7.30.0',
accept: '*/*',
'cache-control': 'no-cache',
'postman-token': '93700e4a-9978-4771-993a-8ecc016d0db5',
host: '127.0.0.1:3000',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive',
'content-length': '50'
}
  1. content-type 是这次请求携带的数据的类型:

    • application/json 表示是一个 json 类型;
    • text/plain 表示是文本类型
    • application/xml 表示是 xml 类型;
    • multipart/form-data 表示是上传文件;
  2. content-length:文件的大小和长度;

  3. connection:keep-alive:

    • http 是基于 TCP 协议的,但是通常在进行一次请求和响应结束后会立刻中断;
    • 在 http1.1 中,所有连接默认是 connection: keep-alive 的;
    • 不同的 Web 服务器会有不同的保持 keep-alive 的时间;
    • Node 中默认是 5s 中;
  4. accept-encoding:告知服务器,客户端支持的文件压缩格式,比如 js 文件可以使用 gzip 编码,对应 .gz 文件;

  5. accept:告知服务器,客户端可接受文件的格式类型;

  6. user-agent:客户端相关的信息;

登录注册案例

仅仅是个小案例,好多没有限制,比如相同的用户名禁止注册。。。

  • 数组方法 find () 返回数组中第一个符合的元素的值 (并且不检查剩余值);参数:(item,index,arr);找不到返回 undefined。
  • 异步写入 writeFile:相对于同步,多了一个回调函数,内置一个参数 err,用于返回错误。
html
1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<!-- action是提交的地址,method是提交的方式,input中的name必须写 -->
<form action="http://127.0.0.1:3000/login" method="GET">
<div>
用户名:<input type="text" name="username">
</div>
<div>
密码:<input type="password" name="password">
</div>
<div>
<input type="submit" value="登录" id="btn">
</div>
</form>
json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[{
"id": 1,
"username": "张三",
"password": 123456,
"isBloack": false
}, {
"id": 2,
"username": "李四",
"password": 123456,
"isBloack": false
}, {
"id": 3,
"username": "王五",
"password": 123456,
"isBloack": true
}, {
"username": "fwd",
"password": "123321",
"id": 4,
"isBloack": false
}, {
"username": "AAS",
"password": "321",
"id": 5,
"isBloack": false
}]
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
const http = require("http");
const fs = require("fs");
const path = require("path");
const url = require("url");
const qs = require("querystring");


let server = http.createServer().listen(3000, "127.0.0.1", () => {
console.log("服务器正在运行...");
})

server.on("request", (req, res) => {
// 获取我们请求的路径
let requestUrl = url.parse(req.url).pathname;
if(requestUrl=="/register.html"){
// 设置头信息
res.writeHead(200,{'Content-type':'text/html;charset=utf-8'});
res.end(thePath("./register.html"))
}else if(requestUrl=="/login.html"){
// 设置头信息
res.writeHead(200,{'Content-type':'text/html;charset=utf-8'});
res.end(thePath("./login.html"))
}else if (requestUrl == "/add") {
// 设置头信息
res.writeHead(200,{'Content-type':'text/html;charset=utf-8'});
// 我们获取路径中的get参数;可以先打印看一下属性,然后确定了用query属性
let reqUrl = url.parse(req.url).query; //'username=zs&password=20'
//处理get参数 将get参数转化成对象格式
let qsResult = qs.parse(reqUrl); //{ username: 'zs', password: '20' }
let jsonArr = JSON.parse(thePath("./member.json")); //默认是字符串,需转为数组(json)
// 完善用户信息
qsResult.id = jsonArr[jsonArr.length - 1].id + 1, //id的话直接用length-1的话,如果前面删除了,则会产生重复的id
qsResult.isBloack = false;
jsonArr.push(qsResult); // jsonArr也就是前端发送的数据(注册数据)追加到数组中
// 因为要看是否存储成功,所以这里得用异步写入 (异步相较于同步多了callback 函数,单个参数 err 和用于返回错误。)
fs.writeFile(path.resolve(__dirname,"./member.json"), JSON.stringify(jsonArr), {
flag: "w",
encoding: "utf-8"
}, error=>{
if (error) res.end("<script>alert('注册失败');location.href='http://127.0.0.1:3000/register.html'</script>");
else res.end("<script>alert('注册成功');location.href='http://127.0.0.1:3000/login.html'</script>");
})
}else if (requestUrl=="/login"){
// 设置头信息
res.writeHead(200,{'Content-type':'text/html;charset=utf-8'})
// 获取路径中的get参数
let reqUrl = url.parse(req.url).query; //'username=zs&password=20'
// 将get参数转为对象格式
let qsResult = qs.parse(reqUrl);
let {username,password}=qsResult;
// 将所有的数据取出
let jsonArr = JSON.parse(thePath("./member.json"));
//查看JSON中是否有用户名和密码 是否吻合
// find 返回数组中第一个符合的元素的值(并且不检查剩余值);参数:(item,index,arr);找不到返回undefined
let loginResult = jsonArr.find(item=>{
return item.username==username;
})
//console.log(loginResult)// { username: 'fwd', password: '123321', id: 4, isBloack: false }
// 判断用户是否存在
if(loginResult==undefined){
res.end("<script>alert('用户不存在');location.href='http://127.0.0.1:3000/register.html'</script>")
}else{
// 判断是否禁用
if (loginResult.isBloack==true){
res.end("<script>alert('账户已被拉黑');location.href='http://127.0.0.1:3000/register.html'</script>")
}else{
if(loginResult.password!=password){
// 判断密码是否正确
res.end("<script>alert('密码错误');location.href='http://127.0.0.1:3000/register.html'</script>")
}else{
res.end("<script>alert('登陆成功');location.href='http://127.0.0.1:3000/register.html'</script>")
}
}
}
}

})


function thePath(absolutePath) {
return fs.readFileSync(path.resolve(__dirname, absolutePath), "utf-8");
}