使用node进行web开发
用户上网流程:
表面上看:打开浏览器——输入网址——跳转——上网。
背后的过程是什么呢?
http请求网址到指定的主机——服务器接收请求——服务器响应内容到用户浏览器——浏览器接收到数据,并根据接收到的信息进行处理,渲染出用户看到的界面。
名词解释:
客户端——用户浏览器;
服务端——服务端
现在再精简下描述:
由客户端发送一个http请求到服务端,服务端接收并处理请求,返回数据到客户端。
搭服务器
收发数据遵循一定的规则————一般是http规则。
搭建服务器,处理http请求,需要node.js提供一个http模块。
//加载一个http模块
var http=require('http');
//通过http模块下的creatServer方法创建并返回一个服务器对象
var server=http.createServer();
//开启服务器,接收四个参数
// 参数第一个是端口,第二个是ip,第三个是链接等待队列的最大长度,第四个成功开启之后的回调
server.listen();
这个时候输入console.log(server.address())
,结果返回:{ address: '::', family: 'IPv6', port: 57379 }
port端口号每次都改变,所以一般要自己设置。端口号尽量设大点。
http - http模块 - require('http')
var http = require('http');
var server = http.createServer([requestListener])
–创建并返回一个HTTP服务器对象
–requestListener : 监听到客户端连接的回调函数
server.listen(port, [hostname], [backlog], [callback])
–监听客户端连接请求,只有当调用了listen方法以后,服务器才开始工作
–port : 监听的端口
–hostname : 主机名(IP/域名)
–backlog : 连接等待队列的最大长度
–callback : 调用listen方法并成功开启监听以后,会触发一个listening事件,callback将作为该事件的执行函数
listening事件:当server调用listen方法并成功开始监听以后触发的事件
error事件 : 当服务开启失败的时候触发的事件
–参数err : 具体的错误对象
全部代码如下,一个服务器就搭建完毕了。
//加载一个http模块
var http=require('http');
//通过http模块下的creatServer方法创建并返回一个服务器对象
var server=http.createServer();
//开启服务器,接收四个参数
// 参数第一个是端口,第二个是ip,第三个是链接等待队列的最大长度,第四个成功开启之后的回调
server.on('error',function (err) {//报错方法
console.log('错误信息:'+err);
});
server.on('listening',function () {
console.log('服务器正在运行...');
console.log(server.address());
});//运行提示
server.listen(8088,'localhost');//绑定8088端口
输出结果:
服务器正在运行...
{ address: '127.0.0.1', family: 'IPv4', port: 8088 }
request事件
在上面的代码插入一段
server.on('request',function () {
console.log('有客户端请求了!')
});
然后通过浏览器输入127.0.0.1:8088,这时弹出会弹出相应的信息。
注意:之前提到的createServer方法。它其实接收一个回调函数,也就是说二者其实是一样的。
request事件 : 当有客户端发送请求到该主机和端口的请求的时候触发
仅仅接收请求是没有多大意义的。还需要对客户端的请求进行分析。
- 参数request : http.IncomingMessage的一个实例,通过他我们可以获取到这次请求的一些信息,比如头信息,数据等。
server.on('request',function (req,res) {
console.log('有客户端请求了!')
console.log(req);
});
重新载入页面,发现第一个参数request是一大串的信息。
里面有许多常用的方法。
- httpVersion : 使用的http协议的版本,通常是1.1
- headers : 请求头信息中的数据
- url : 请求的地址
method : 请求方式(GET/POST)
- 参数response : http.ServerResponse的一个实例,通过他我们可以向该次请求的客户端输出返回响应
比如说请求发过来了,但是客户端一直在等待,而服务器未向客户端发送任何数据。可以通过server.timout
方法设置最长等待时间。 - write(chunk, [encoding]) : 发送一个数据块到响应正文中,数据块,编码
- end([chunk], [encoding]) : 当所有的正文和头信息发送完成以后调用该方法告诉服务器数据已经全部发送完成了,这个方法在每次完成信息发送以后必须调用,并且是最后调用.
server.on('request',function (req,res) {
console.log('有客户端请求了!');
res.write('朕知道你发请求了','utf-8');//光是写入还不行,必须告诉客户端写完了
res.end();
//console.log(res);
});
客户端再刷新的结果,厉害了:
你也可以在你的文档里写如一段html代码。还可以设置头信息。
- statusCode : 该属性用来设置返回的状态码
- setHeader(name, value) : 设置返回头信息
- writeHead(statusCode, [reasonPhrase], [headers])//状态码(200,404等),
状态码可以通过输入http.STATUS_CODES
点击进行查看
这个方法只能在当前请求中使用一次,并且必须在response.end()之前调用res.writeHead(200,'SUNNY');//定义成功时的描述文字
请求参数req里的url处理
上面的服务器有重大的缺陷。url无论输入localhost/1....后面长度无论怎样,返回的都是一个信息。
以下我们用case来模拟首页('/'),个人中心('/user')和404('defult')的响应情况
新建一个服务器。
var http=require('http');
var server=http.createServer();
server.on('request',function (req,res) {
//通过req的url属性回应数据
//?后面的部分是query string查询字符串
console.log(req.url)
});
server.listen(8089,'localhost');
当访问localhost:8089/xxx
时,打印出来的url为/xxx
再分析下url对象这个东西。路径名有时候是非常1复杂的,包含?等等参数。对url进行预处理很有必要,这就涉及到url对象
var http=require('http');
//请求url模块,并把url序列化想要的格式
var url=require('url');
var server=http.createServer();
server.on('request',function (req,res) {
//通过req的url属性回应数据
//?后面的部分是query string查询字符串
//console.log(req.url)
var urlStr=url.parse('http://www.baidu.com/a/index.html?c=2');
console.log(urlStr);
});
server.listen(8089,'localhost');
得到的打印出来的是
分析上面的内容发现,我们需要urlStr.pathname
var http=require('http');
//请求url模块,并把url序列化想要的格式
var url=require('url');
var server=http.createServer();
server.on('request',function (req,res) {
//通过req的url属性回应数据
//?后面的部分是query string查询字符串
//console.log(req.url)
var urlStr=url.parse(req.url);
//console.log(urlStr);
switch(urlStr.pathname){
case '/':
//首页,没有什么后缀
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
res.end('<h1>首页</h1>');
break;
case '/user':
//用户首页
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
res.end('<h1>个人中心</h1>');
break;
default:
res.writeHead(404,{
'content-type':'text/html;charset=utf-8'
});
res.end('<h1>404</h1>');
//处理其他情况
break;
}
});
server.listen(8089,'localhost');
那么url处理就算成功了。
表现和行为的分离
上面的代码中,每次针对每个页面输入不一样的内容,看起来是非常麻烦的。
作为规则,把html写进代码也是不符合规范的。也不利于维护。
所以我们需要用res的方法把它读取出来,再放到页面上去。
可以在同目录下新建一个html文件夹,创建首页(index.html)、用户中心(user.html)、404.html并写上各自的内容。然后用fs对象来读它。
var http=require('http');
var url=require('url');
var fs=require('fs');
var server=http.createServer();
var htmlDir='./html/';
server.on('request',function (req,res) {
var urlStr=url.parse(req.url);
//console.log(urlStr);
switch (urlStr.pathname){
case '/':
sendData(htmlDir+'index.html',req,res);
break;
case '/user':
sendData(htmlDir+'user.html',req,res);
break;
default:
sendData(htmlDir+'404.html',req,res);
break;
}
});
//应该定义一个方法,当根据路径来读取页面,然后把所需要的页面返回出去。
//三个参数:file是文件路径
function sendData(file,req,res) {
//这个方法的核心是读取文件内容。
fs.readFile(file,function (err,data) {
if(err){//如果读取失败,也就是404
console.log(err);
res.writeHead(404,{
'content-type':'text/html;charset=utf-8'
});
res.end(data);
}else{
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
//console.log(data.toString());
res.end(data);
}
});
}
server.listen('8089','localhost');
效果就实现了。但实际上代码还存在很多不必要的case语句。尝试再修改:
var http=require('http');
var url=require('url');
var fs=require('fs');
var server=http.createServer();
var htmlDir='./html';
server.on('request',function (req,res) {
var urlStr=url.parse(req.url);
switch (urlStr.pathname){
case '/':
sendData(htmlDir+'/index.html',req,res);
break;
default:
var urlPath=htmlDir+urlStr.pathname+'.html';
sendData(urlPath,req,res);
}
});
//应该定义一个方法,当根据路径来读取页面,然后把所需要的页面返回出去。
//三个参数:file是文件路径,
function sendData(file,req,res) {
//这个方法的核心是读取文件内容。
fs.readFile(file,function (err,data) {
if(err){//如果读取失败,也就是404
console.log(file)
console.log(err);
fs.readFile('./html/404.html',function(err,data){
res.writeHead(404,{
'content-type':'text/html;charset=utf-8'
});
res.end(data);
});
}else{
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
//console.log(data.toString());
res.end(data);
}
});
}
server.listen('8089','localhost');
在这个代码下,尝试读取任何页面都有回应。我在html文件夹下创建了一个新的xxx.html读取也能正确。代码也简化了。这样基本效果就令人满意了。
遗憾的是当前还不支持css,js外链。
请求处理
为了说明原理,还是采用上面的例子代码。
- get请求的数据处理
在html文件夹下创建一个login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<h1>这是登陆页面</h1>
<form action="/login/validate" method="get">
请输入用户名:<input type="text" name="username"><br>
请输入密码:<input type="text" name="password"><br>
<button type="submit">提交!</button>
</form>
</body>
</html>
如何获取用户提交过来的内容呢?在之前声明过的urlStr里面就包含了这些数据。
var http=require('http');
var url=require('url');
var fs=require('fs');
var server=http.createServer();
var htmlDir='./html';
server.on('request',function (req,res) {
var urlStr=url.parse(req.url);
switch (urlStr.pathname){
case '/':
sendData(htmlDir+'/index.html',req,res);
break;
case '/login/validate':
//想得到用户提交过来的数据
//console.log(req.method)//提交方式get/post
console.log(urlStr);
break;
default:
var urlPath=htmlDir+urlStr.pathname+'.html';
sendData(urlPath,req,res);
}
});
function sendData(file,req,res) {
//这个方法的核心是读取文件内容。
fs.readFile(file,function (err,data) {
if(err){//如果读取失败,也就是404
console.log(file)
console.log(err);
fs.readFile('./html/404.html',function(err,data){
res.writeHead(404,{
'content-type':'text/html;charset=utf-8'
});
res.end(data);
});
}else{
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
//console.log(data.toString());
res.end(data);
}
});
}
server.listen('8089','localhost');
登陆locoalhost:8089/login
输入用户名密码,提交,发现打印出下列内容:
其中关键属性是query。
原来用户名是213,密码是231.
怎么实现自动化处理呢?node.js提供了必要的方法。
querystring模块
parse() : 将一个 query string 反序列化为一个对象
case '/login/validate':
//想得到用户提交过来的数据
//console.log(req.method)//提交方式get/post
var querystring=require('querystring');
console.log(querystring.parse(urlStr.query));
break;
结果打印出来一个json:{ username: '213', password: '231' }
实在是太方便了。有了它,就可以很方便的处理提交信息。
- post请求的数据处理:req的data和end事件
接下来把login.html页面的提交方式改为post
post发送的数据会被写入缓冲区中,需要通过resquest的data事件和end事件来进行数据拼接处理
if(req.method.toUpperCase()=='POST'){
var str='';
req.on('data',function (chunk) {
str+=chunk;
});
req.on('end',function () {
console.log(str);
})
}
打印出来的str为username=123&password=321
对于这样格式写成的数据自然想到了用querystring.parse()进行处理。处理完之后就是一个json对象了。
全部代码:
var http=require('http');
var url=require('url');
var fs=require('fs');
var server=http.createServer();
var htmlDir='./html';
server.on('request',function (req,res) {
var urlStr=url.parse(req.url);
switch (urlStr.pathname){
case '/':
sendData(htmlDir+'/index.html',req,res);
break;
case '/login/validate':
//想得到用户提交过来的数据
//console.log(req.method)//提交方式get/post
if(req.method.toUpperCase()=='POST'){
var str='';
req.on('data',function (chunk) {
str+=chunk;
});
req.on('end',function () {
console.log(str);
var querystring=require('querystring');
console.log(querystring.parse(str));
});
}else if(req.method.toUpperCase()=='GET'){
var querystring=require('querystring');
console.log(querystring.parse(urlStr.query));
}
break;
default:
var urlPath=htmlDir+urlStr.pathname+'.html';
sendData(urlPath,req,res);
}
});
function sendData(file,req,res) {
//这个方法的核心是读取文件内容。
fs.readFile(file,function (err,data) {
if(err){//如果读取失败,也就是404
console.log(file)
console.log(err);
fs.readFile('./html/404.html',function(err,data){
res.writeHead(404,{
'content-type':'text/html;charset=utf-8'
});
res.end(data);
});
}else{
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
});
//console.log(data.toString());
res.end(data);
}
});
}
server.listen('8089','localhost');