NodeJs服务器

创建服务器

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

  • method1:
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:(事件驱动方式)
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() 响应结束的信号 ,只能有一个,这种方式是写出最后的数据,并且写出后会关闭流;
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);
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;
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标签与设置状态码。

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文件:

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>
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
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");
})

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

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模块来进行参数的管理;

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的界面渲染上去;

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”, () => {});
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中也包含很多有用的信息,客户端会默认传递过来一些信息:

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,用于返回错误。
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>
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
}]
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");
}