node.js的基本学习(二)——web服务器与GET/POST处理

前言:

承接上一节的内容,继续往下学习

 

 

 

 


web服务器:

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web服务器的基本功能就是提供Web信息浏览服务。它只需支持HTTP协议、HTML文档格式及URL,与客户端的网络浏览器配合。

在nodejs中,可以用短短几行代码就写出一个服务器:

//导入系统核心库——http
var http=require('http');

//createServer内部函数有两个参数
//一个是请求,一个是响应
//这两个参数实现的是‘流’,是流的实例
var server=http.createServer(function(request,response){
    console.log('请求已经接收');
    //将response像流那样操作,用一个写入流
    //写头部流信息(参数是状态码和给浏览器传的内容类型)
    response.writeHead(200,{'Content-Type':'text/plain'});
    //服务器端向客户端(浏览器)写的文本内容
    response.write('Do you love me?');  
    response.end();
})
//让服务器监听在一个端口上
server.listen(3000);
console.log("服务器监听在3000端口");

控制台会发现输出了两次:请求已经接收

因为浏览一次,客户端发送了两个请求

测试-返回一个JSON:

//导入系统核心库——http
var http=require('http');

//createServer内部函数有两个参数
//一个是请求,一个是响应
//这两个参数实现的是‘流’,是流的实例
var server=http.createServer(function(request,response){
    console.log('请求已经接收');
    //将response像流那样操作,用一个写入流
    //写头部流信息(参数是状态码和给浏览器传的内容类型)
    response.writeHead(200,{'Content-Type':'application/json'});
    //一个json新对象
    var myObj={
        name:"luoluo",
        job:"actor",
        age:21
    }
    //stringify把myObj变成一个字符串类型的json
    //即序列化
    response.write(JSON.stringify(myObj));
    
    response.end();
})
//让服务器监听在一个端口上
server.listen(3000);
console.log("服务器监听在3000端口");

在响应http界面:

#test.html
<!DOCTYPE html>   <!--html5标准网页声明-->
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
        <table border="1" width="95%" cellspacing="0" cellpadding="3">
            <tbody>
                <tr align="center">
                    <td width="80">英雄</td>
                    <td> 技能</td>
                </tr>
                <tr align="center">
                    <td width="80">超人</td>
                    <td> 钢铁之躯</td>
                </tr>
                <tr align="center">
                    <td width="80">蝙蝠侠</td>
                    <td> 意志力</td>
                </tr>
                <tr align="center">
                    <td width="80">神器女侠</td>
                    <td> 真言套索</td>
                </tr>
            </tbody>
        </table>
    </body>
</html>


#app.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');

//createServer内部函数有两个参数
//一个是请求,一个是响应
//这两个参数实现的是‘流’,是流的实例
var server=http.createServer(function(request,response){
    console.log('请求已经接收');
    //将response像流那样操作,用一个写入流
    //写头部流信息(参数是状态码和给浏览器传的内容类型)
    response.writeHead(200,{'Content-Type':'text/html'});
    var ReadStream=fs.createReadStream(__dirname+"/test.html",'utf8');
    //利用管道直接写入response
    ReadStream.pipe(response);
})
//让服务器监听在一个端口上
server.listen(3000);
console.log("服务器监听在3000端口");

利用前面学的模块去重构文件:

#test.html
还和上面一样

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
function startServer(){
    var server=http.createServer(function(request,response){
        console.log('请求已经接收');
        response.writeHead(200,{'Content-Type':'text/html'});
        var ReadStream=fs.createReadStream(__dirname+"/test.html",'utf8');
        ReadStream.pipe(response);
    })
    server.listen(3000);
    console.log("服务器监听在3000端口");
}
exports.startServer=startServer;

#app.js
var server=require('./server');
//server现在是一个对象
server.startServer();

 

 


路由:

路由:URL到函数的映射,或者说是请求资源的路标

要获得客户端的url信息,就要利用request

有心的朋友尝试输出一下我们的request.url就会发现它输出了一个 /

即127.0.0.1:3000对应的就是根目录

在上面的基础上,我们修改server.js,用if进行不同处理即可

//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
function startServer(){
    var server=http.createServer(function(request,response){
        console.log('请求已经接收'+request.url);
        if(request.url==='/'||request.url==='/home')
        {
            response.writeHead(200,{'Content-Type':'text/html'});
            fs.createReadStream(__dirname+"/index.html",'utf8').pipe(response);
        }else if(request.url==='/review'){
            response.writeHead(200,{'Content-Type':'text/html'});
            fs.createReadStream(__dirname+"/review.html",'utf8').pipe(response);
        } else if(request.url==='/api/v1/records'){
            response.writeHead(200,{'Content-Type':'application/json'});
            var jsonObj={
                name:"小明",
            }
            //把对象序列化
           response.end(JSON.stringify(jsonObj));
        }else{
            response.writeHead(200,{'Content-Type':'text/html'});
            fs.createReadStream(__dirname+"/404.html",'utf8').pipe(response);
        }
    })
    server.listen(3000);
    console.log("服务器监听在3000端口");
}
exports.startServer=startServer;

如此一来,就处理了127.0.0.1:3000、127.0.0.1:3000/home、127.0.0.1:3000/review、127.0.0.1:3000/api/v1/records还有其他url的404情况

nodejs中的等号很有趣,两个等号只是判断外在的值是否相等,不考虑类型,三个等号就是考虑类型的情况了(例如:1==”1″,返回true;1===”1″,返回false)

为了方便整理,我们来重构一下路由代码

做个route函数,先弄成这样

#route.js
function route(pathname){
    console.log("Routing a request for"+pathname);
}
module.exports.route=route;

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');

function startServer(route){
    var onRequest=function(request,response){
        console.log('接收到了'+request.url);
        route(request.url);
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer;


#app.js
//注意此时导出的是个对象
var server=require('./server');
var route=require('./route');
//server现在是一个对象
server.startServer(route.route);

我们再新建一个handler,js,在这个文件里进行处理

#route.js
var fs=require("fs");
function route(handle,pathname,response){
    //typeof是用于判断类型
    if(typeof handle[pathname]==='function'){
        //传给handler的各位
        handle[pathname](response);
    }else{
        response.writeHead(200,{'Content-Type':'text/html'});
        fs.createReadStream(__dirname+"/404.html",'utf8').pipe(response);
    }
}
module.exports.route=route;


#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');

function startServer(route,handle){
    var onRequest=function(request,response){
        //传给route
        route(handle,request.url,response);
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer;


#handler.js
var fs=require('fs');

function home(response){
    response.writeHead(200,{'Content-Type':'text/html'});
    fs.createReadStream(__dirname+"/index.html",'utf8').pipe(response);
}
function review(response){
    response.writeHead(200,{'Content-Type':'text/html'});
    fs.createReadStream(__dirname+"/review.html",'utf8').pipe(response);
}
function api(response){
    response.writeHead(200,{'Content-Type':'application/json'});
    var jsonObj={
        name:"小明",
    }
    //把对象序列化 
   response.end(JSON.stringify(jsonObj));
}
module.exports={
    home:home,
    review:review,
    api:api
}

#app.js
//注意此时导出的是个对象
var server=require('./server');
var route=require('./route');
var handler=require('./handler');

//建立一个空对象并且给空对象添加值
//key是路径,value是handler里面的函数
var handle={};
handle['/']=handler.home;
handle['/home']=handler.home;
handle['/review']=handler.review;
handle['/api/v1/records']=handler.api;

//server现在是一个对象
server.startServer(route.route,handle);

另外涉及到的三个html文档就不写出来了

至此,原先的路由代码就重构完成,以后添加删除都很方便了

 


GET或POST传递数据:

客户端发送请求一般有两种方式:在url地址中传递参数,或者通过表单去提交数据,比较常见的就是url带参的GET和提交表单的POST

GET:

例如:http://www.xxx.net/articles?kind=hot 中的 kind=hot ,就是给网站传了一个键值。

在上面的操作中,我们有很多路径,若访问者加上了GET参数(加上?a=b),我们要怎么舍去?a=b,还原路径呢?只要在上面的server.js中加上这几句就好了:

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
//url工具库,可以对url进行处理
var url=require('url');

function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        //传给route
        route(handle,pathname,response);
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer;

然后我们访问127.0.0.1:3000/review?a=b就等同于访问127.0.0.1:3000/review

那要怎么取出来?a=b呢?

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
var url=require('url');

function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        //处理url后面的参数
        var params=url.parse(request.url,true).query;
        //传给route
        route(handle,pathname,response,params);
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer; 

url.parse()可以将一个完整的URL地址,分为很多部分,常用的有:host、port、pathname、path、query.

第二个参数为true,query属性会生成一个对象,如果为false,则返回url对象上的query属性会是一个未解析,未解码的字符串,默认为false

另外,parse的意思是解析,在c#中parse是强制类型转换的方法

如此一来,后面的route,handler的各个函数,都要加上形参param,然后我们就可以在handler中得到用户传来的param字段,然后根据需求进行处理。

我们就得到了Get请求的字段

 

POST:

表单传输数据,例如登录框(打开审查元素即可发现method=”post”)

先构造一个可以发送POST请求的html页面

#review.html
<!DOCTYPE html>
<html lang="en"> 
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
        <!--action属性指定向何处发送表单-->
        <form action="/api/v1/records" method="POST">
            姓名:<input type="text" name="name"/>
            年龄:<input type="text" name="age"/>
            <input type="submit" value="提交">
        </form>
    </body>
</html>

注意,表单发送到了/api/v1/records。然后处理其他js代码:

#serverjs
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
var url=require('url');

function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        var data="";
        //request是一个流,所以他也是继承事件的一个实例
        request.on("error",function(err){
            console.error(err);  //错误的时候显示
        }).on("data",function(chunk){
            data+=chunk;    //不断接收数据
        }).on("end",function(){  //传给route
            route(handle,pathname,response,data);
        });
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer; 


#handler.js
var fs=require('fs');

function home(response,param){
    response.writeHead(200,{'Content-Type':'text/html'});
    fs.createReadStream(__dirname+"/index.html",'utf8').pipe(response);
}
function review(response,param){
    response.writeHead(200,{'Content-Type':'text/html'});
    fs.createReadStream(__dirname+"/review.html",'utf8').pipe(response);
    console.log(param);
}
function api(response,param){
    response.writeHead(200,{'Content-Type':'application/json'});
    //把对象序列化 
   response.end(JSON.stringify(param));
}
module.exports={
    home:home,
    review:review,
    api:api
}

#route.js
var fs=require("fs");
function route(handle,pathname,response,params){
    //typeof是用于判断类型
    if(typeof handle[pathname]==='function'){
        //传给handler的各位
        handle[pathname](response,params);
    }else{
        response.writeHead(200,{'Content-Type':'text/html'});
        fs.createReadStream(__dirname+"/404.html",'utf8').pipe(response);
    }
}
module.exports.route=route; 

#app.js
//注意此时导出的是个对象
var server=require('./server');
var route=require('./route');
var handler=require('./handler');

//建立一个空对象并且给空对象添加值
//key是路径,value是handler里面的函数
var handle={};
handle['/']=handler.home;
handle['/home']=handler.home;
handle['/review']=handler.review;
handle['/api/v1/records']=handler.api;

//server现在是一个对象
server.startServer(route.route,handle);

访问127.0.0.1:3000/review后填写数据,我们会跳转到/api/v1/records,并看见我们填写的数据以字符串形式出现(说明我们传给/api/v1/records的是个字符串)。

字符串显然是不能被利用的,接下来我们解析信息:

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
var url=require('url');
var querystring=require('querystring');


function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        var data="";
        //request是一个流,所以他也是继承事件的一个实例
        request.on("error",function(err){
            console.error(err);  //错误的时候显示
        }).on("data",function(chunk){
            data+=chunk;    //不断接收数据
        }).on("end",function(){  //传给route
             //利用querystring.parse处理字符串数据
            route(handle,pathname,response,querystring.parse(data));
        });
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer; 

考虑到POST和GET都有的情况,我们可以这样:

#server.js
//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
var url=require('url');
var querystring=require('querystring');


function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        var data="";
        //request是一个流,所以他也是继承事件的一个实例
        request.on("error",function(err){
            console.error(err);  //错误的时候显示
        }).on("data",function(chunk){
            data+=chunk;    //不断接收数据
        }).on("end",function(){  //传给route
            if(request.method==="POST")
                route(handle,pathname,response,querystring.parse(data));
            else{
                var params=url.parse(request.url,true).query;
                route(handle,pathname,response,params);
            }
        });
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer; 

最后,用Buffer.concat方法处理data,并且对POST表单长度做限制之后,最后的server就是这样:

//导入系统核心库——http
var http=require('http');
//核心库(fs)
var fs=require('fs');
var url=require('url');
var querystring=require('querystring');


function startServer(route,handle){
    var onRequest=function(request,response){
        //只获取请求的路径名
        var pathname=url.parse(request.url).pathname;
        var data=[];
        //request是一个流,所以他也是继承事件的一个实例
        request.on("error",function(err){
            console.error(err);  //错误的时候显示
        }).on("data",function(chunk){
            data.push(chunk);    //不断接收数据
        }).on("end",function(){  //传给route
            if(request.method==="POST")
            {    //表单内容过多则加以限制
                if(data.length>1e6)
                    request.connection.destroy();
                data=Buffer.concat(data).toString();
                route(handle,pathname,response,querystring.parse(data));
            }
            else{
                var params=url.parse(request.url,true).query;
                route(handle,pathname,response,params);
            }
        });
    }
    var server=http.createServer(onRequest);

    server.listen(3000);
    console.log("服务器监听在3000端口");
}
module.exports.startServer=startServer; 

 

 

 

 


 

 

 

 

 

 

 

 

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

发表评论