2018-11-26 node.js11-处理get和post请求-进阶
- 背景介绍
- 简单实现
- 结合http服务的实现
- 封装路由实现http服务
2018-11-19 node.js10-处理get和post请求 介绍了如何利用node.js来实现处理get和post请求,并且进行了模块化封装。
不过在这个例子中,所有路由都是封装在app这一个模块中,可读性和可维护性也并不好。
我们可以参照node.js的web框架express的路由处理方法来进一步修改。
在 Express basic routing | Express 中文文档 中截取一段代码来说明一下express路由的实现原理
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user')
})
从代码中可以清楚了解到,这段代码是处理
- put方法
- user路由
- 处理函数代码是res.send()
这种路由表定义方式,用于执行不同的 HTTP 请求动作显然简单易读,易扩展。
【简单实现】
参照上面代码,我们可以先做一个简单例子
var G={};
var app=function(req,res){
if(G['login']){
G['login'](req,res); /*执行注册的方法*/
//定义一个post方法
app.post=function(string,callback){
G[string]=callback;
//执行post方法
app.post('login',function(req,res){
console.log('login '+req);
setTimeout(function(){
app('req','res');
},1000);
说明如下
- 代码中定义了一个集合对象G
- 定义app方法,其中代码:如果数组对象G['login']存在,那么执行G['login']定义的方法
- 定义app.post方法,它有2个参数,一个是string变量,另外一个是回调函数。注意:app其实也是对象,所以可以对其定义方法。
- 然后顺序执行app.post方法,根据传入的参数,在这个方法中注册了一个'login'路由,及其对应的处理函数
- 最后设置定时执行app方法
运行后,执行结果如下
login req
[Finished in 1.3s]
由于注册了'login'路由,也就是在app.post中产生了G['login'],及其对应处理函数,所以在执行app方法时候,发现G['login']存在,就执行了它对应的回调函数 console.log('login '+req);。
这样也就模仿express封装路由方式完成了一个最简单的实现。
【结合http服务的实现】
把上面简单实现代码引入http服务,就可以完成和 2018-11-19 node.js10-处理get和post请求 中完全相同的处理get和post请求功能
var http=require('http');
var url=require('url');
var G={};
//定义方法开始结束
var app=function(req,res){
var pathname=url.parse(req.url).pathname;
if(!pathname.endsWith('/')){
pathname=pathname+'/';
if(G[pathname]){
G[pathname](req,res); /*执行注册的方法*/
}else{
res.end('no router');
//定义一个post方法
app.post=function(string,callback){
if(!string.endsWith('/')){
string=string+'/';
if(!string.startsWith('/')){
string='/'+string;
// /login/
G[string]=callback;
//只要有请求 就会触发app这个方法
http.createServer(app).listen(8001);
//注册login这个路由(方法)
app.post('login',function(req,res){
console.log('login');
res.end('login');
app.post('register',function(req,res){
console.log('register');
res.end('register');
})
说明
- 代码中引入了http模块和url模块
- 先看代码核心,定义处理post请求的方法app.post。这里做了一个标准化操作,注册的路由前后都必须有斜杠。比如,输入的是login,实际注册的就是/login/。
- 然后app.post方法注册了2次,分别注册了/login/和/register/路由。这2个路由执行函数的效果,也就是在客户端页面上分别显示login和register。
- 再看app方法,它对客户端输入的地址也做了标准化处理,必须以斜杠结尾。然后分析路由,如果注册过,执行对应的函数;如果没有注册过,那么在客户端页面上显示'no router'。
- 最后在CreateServer方法中把app对象传入,监听在8001端口。
【封装路由实现http服务】
现在我们可以利用以上方法,模拟express的路由原理来重新实现 2018-11-19 node.js10-处理get和post请求
代码如下:
get_post.js
var http=require('http');
var ejs=require('ejs');
var app=require('./model.js');
console.log(app);
http.createServer(app).listen(8001);
console.log('Server running at http://127.0.0.1:8001/');
//注册get方法根目录处理方法
app.get('/',function(req,res){
var msg='这是根目录'
ejs.renderFile('views/index.ejs',{msg:msg},function(err,data){
res.send(data);
//注册get方法login目录处理方法
app.get('/login',function(req,res){
ejs.renderFile('views/form.ejs',{},function(err,data){
console.log('login');
res.send(data);
//执行登录,注册dologin的处理方法
app.post('/dologin',function(req,res){
console.log('post传递过来的数据为:'+req.body); /*获取post传过来的数据*/
res.send("<script charset='utf-8' type='text/javascript' >alert('login successful!');history.back();</script>")
//注册get方法register目录处理方法
app.get('/register',function(req,res){
console.log('register');
res.send('register');
})
model.js
var url=require('url');
//封装方法改变res 绑定res.send()
function changeRes(res){
res.send=function(data){
//封装请求头(解决中文显示)和end
res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});
res.end(data);
//暴露的模块
var Server=function(){
var G=this; /*全局变量*/
//处理get和post请求
this._get={};
this._post={};
var app=function(req,res){
//封装请求头(解决中文显示)和end
changeRes(res);
//获取路由
var pathname=url.parse(req.url).pathname;
console.log('pathname='+pathname);
if(!pathname.endsWith('/')){
pathname=pathname+'/';
//获取请求的方式 get or post
var method=req.method.toLowerCase();
console.log('method='+method);
if (pathname!='favicon.ico'){
if(G['_'+method][pathname]){
if(method=='post'){ /*执行post请求*/
var postStr='';
req.on('data',function(chunk){
postStr+=chunk;
req.on('end',function(err,chunk) {
req.body=postStr; /*给req增加了body属性,表示拿到post的值*/
//G._post['dologin'](req,res)
G['_'+method][pathname](req,res); /*执行方法*/
}else{ /*执行get请求*/
//console.log('pathname='+pathname);
console.log('执行方法:'+G['_'+method][pathname]);
G['_'+method][pathname](req,res); /*执行方法*/
}else{
res.end('no router');
}catch(err){
res.end('no router');
app.get=function(string,callback){
if(!string.endsWith('/')){
string=string+'/';
if(!string.startsWith('/')){
string='/'+string;
// /login/
G._get[string]=callback;
app.post=function(string,callback){
if(!string.endsWith('/')){
string=string+'/';
if(!string.startsWith('/')){
string='/'+string;
// /login/
G._post[string]=callback;
//G._post['dologin']=function(req,res){
return app;
module.exports=Server();//暴露外部访问接口
整个代码包含2部分功能
1.路由注册
- model.js中包含app.post和app.get这2个注册方法,在上面“结合http服务实现”中已经介绍过。需要2个参数,字符串和回调函数。这里做了一个标准化操作,注册的路由前后都必须有斜杠。
- get_post.js中调用model.js的app.post和app.get方法注册了3个get请求和1个post请求路由。
2.http请求处理
以 127.0.0.1:8001/login为例,处理过程如下
step1.接收到请求后,先获取路由var pathname=url.parse(req.url).pathname;,得到/login。
step2.获取请求的方式method=req.method.toLowerCase();,得到get
step3.过滤无效的favicon.ico请求后,判断G._get['login']是否存在
step4.存在,那么执行以下代码,调用ejs模块-form.ejs
ejs.renderFile('views/form.ejs',{},function(err,data){
console.log('login');
res.send(data);