一个Web应用可能有如下需求:
- 请求方法的判断。
- URL的路径解析。
- Cookie的解析。
- Basic认证。
- 表单数据的解析。
- 任意格式文件的上传处理。
- Session(会话)的需求。
Node
提供的底层API
相对来说比较简单,但是要完成业务需求,仅仅一个request
事件无法满足如上需求。
我们先看如下函数:
function(req, res){
res.writeHead(200,{'Content-Type':'text/plain'});
res.end();
};
应用上可能无限的复杂,但是只要最终结果返回一个上面的函数作为参数,传递给createServer()
方法作为request
事件的侦听器就可以了。
在具体业务开始前,需要为业务预处理一些细节,这些细节将会挂在在req或者res对象上,供业务代码使用。
1. 请求方法
我们最常见的请求方法是GET
和POST
,除此之外,还有HEAD、DELETE、PUT、CONNECT
等方法。
> GET /path?foo=bar HTTP/1.1
> Host: 127.0.0.1:13337
> User-Agent: curl/7.55.1
> Accept: */*
HTTP_Parser
在解析请求报文的时候,将报文头抽取出来,设置为req.method
。在RESTful类web服务中请求方法十分重要,因为他会决定资源的操作行为。PUT
代表心箭一个资源,POST
表示更新一个资源,GET
表示查看一个资源,而DELETE
表示删除一个资源。
我们可以通过请求方法来决定响应行为,如下:
function(req, res) {
switch (req.method) {
case 'POST':
update(req, res);
break;
case 'DELETE':
remove(req, res);
break;
case 'PUT':
create(req, res);
break;
default:
get(req, res);
}
}
上述代码表示了一种根据请求方法将复杂的业务逻辑分发的思路,是一种化繁为简的方式。
2. 路径解析
除了根据请求方法来进行分发外,路径的判断最常见了。路径部分在报文的第一行的第二部分:
> GET /path?foo=bar HTTP/1.1
HTTP_Parser
将其解析为req.url
,完整的url地址是如下这样。
https://www.jianshu.com/path?foo=bar&query=string
客户端代理(浏览器)会将这个地址解析成报文,将路径和查询部分放在报文第一行。需要注意的是,hash
部分会被丢弃,不会存在报文的任何地方。
最常见的根据路径进行业务处理的应用是静态文件服务器,他会根据路径去查找磁盘中的文件,然后将其响应给客户端,如下:
function(req, res) {
var pathname = url.parser(req.url).pathname;
fs.readFile(path.join(ROOT, pathname), function (err, file) {
if (err) {
res.writeHead(404);
res.end('找不到相关文件。');
return;
}
res.writeHead(200);
res.end(file);
})
}
还有一种比较常见的分发场景是根据路径来选择控制器,它预设路径为控制器和行为的组合,没有配置路径信息,如下:
/controller/action/a/b/c
controller会对应到一个控制器,action对应到控制器的行为,剩余的值会作为参数进行一些别的判断:
function(req, res) {
var pathname = url.parser(req.url).pathname;
var paths = pathname.split('/');
var controller = pathname[1] || 'index';
var args = paths.slice(3);
if (handles[controller] && handles[controller][action]) {
handles[controller][action].apply(null, [req, res].concat(args));
} else {
res.writeHead(500)
res.end('找不到响应控制器')
}
}
这样业务部分可以之关心具体的业务实现,如下:
handles.index = {};
handles.index.index = function(req,res,foo,bar){
res.writeHead(200);
res.end(foo)
};
3. 查询字符串
查询字符串位于玲之后,在地址栏中路径后的foo=bar&query=string
字符串就是查询字符串。
它跟随在路径后,行程请求报文首行的第二部分。该部分经常需要为业务逻辑所用,Node提供了querystring
模块用于处理这部分数据,如下:
var url = require('url');
var querystring = require('querystring');
var query = querystring.parse(url.parse(req.url).query);
更简洁的方法是给url.parser()
传递第二个参数,如下:
var query = url.parser(req.url,true).query;
它会将foo=bar&baz=val
解析为一个JSON
对象,如下:
{
foo: bar,
baz: 'val'
}
在业务调用产生之前,我们的中间件或者框架会查询字符串转换,然后挂在在请求对象上供业务使用,如下:
function(req,res){
req.query = url.parser(req.url, true).query;
hande(req,res);
}
注意点:如果查询字符串中的键出现对此,那么它的值会是一个数组,如下:
//foo=bar&foo=baz
var query = url.parse(req.url,true).query;
网友评论