添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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