• 构建web应用


    一、web服务器示例
    var http = require('http');
    http.createServer(function(req, res){
        res.writeHeader(200, {Content-Type : 'text/plain'});
        res.end('hello world!');
    }).listen(80);
    二、web应用中常见需求:
    (1)解析请求方法(POST GET)
    (2)解析路径
    (3)解析查询字符串
    (4)解析cookie
    (5)Basic认证
    (6)解析表单数据
    (7)文件上传
     
    三、解析请求方法
    function(req, res){
        var method = req.method;
        if(method === 'POST'){
        }else if(method === 'GET'){
        }else if(method === 'DELETE'){
        }else if(method === 'PUT'){
        }else{
        }
    }
    四、解析路径
    (1)返回静态资源
    var url = require('url');
    var fs = require('fs');
    function(req, res){
        var pathname = url.parse(req.url).pathname;
        fs.readFile(path.join(ROOT, pathname), function(err, file){
            if(err){
                res.writeHeader(404);
                res.end('not Found File');
                return;
            }
            res.writeHeader(200);
            res.end(file);
        });
    }
     
    (2)选择控制器:/controller/action/a/b/c
    function(req, res){
        var pathname = url.parse(req.url).pathname;
        var paths = pathname.split('/');
        var contronller = paths[1] || 'index';
        var action = paths[2] || 'index';
        var args = paths.slice(3);
        if(handler[contronller] && handler[contronller][action]){
            handler[contronller][action].apply(null, [req, res].concat(args));
        }else{
            res.writeHeader(500);
            res.end('找不到响应控制器');
        }
    }
    四、查询字符串
    var url = require('url');
    var query = url.parse(req.url, true).query;//{foo : 'aaa', baz : 'bbb'}
    req.query = query;
    handle(req, res);
    五、cookie
    function parseCookie(cookie){
        var cookies = {};
        if(!cookie){
            return cookies;
        }
        var list = cookie.split(';');
        for(var i = 0,len = list.length;i < len;i++){
            var pair = list[i].split('=');
            cookies[pair[0].trim()] = pair[1];
        }
        return cookies;
    };
    req.cookies = parseCookie(req.headers.cookie);
    handle(req, res);
     
    function serialize(name, value, options){
        var pairs = [name+'='+value];
        if(options.expires){
            pairs.push('Expires='+options.expires.toUTCString());
        }
        if(options.maxAge){
            pairs.push('Max-Age='+options.maxAge);
        }
        if(options.path){
            pairs.push('Path='+options.path);
        }
        if(options.domain){
            pairs.push('Domain='+options.domain);
        }
        if(options.httpOnly){
            pairs.push('HTTPOnly');
        }
        if(options.secure){
            pairs.push('Secure');
        }
        return pares.join(';');
    }
     
    res.writeHeader('Set-Cookie', serialize({isVisit : 1}));
    六、Session
    Cookie缺点:
    (1)可在前端改变cookie的值(HTTPOnly可限制前端改变cookie),易篡改。
    (2)隐私信息无法存cookie
    (3)占用网络带宽(每次请求,相关path下的cookie信息都会在请求头中携带,虽然服务端有时候并不需要所有的cookie信息)
    基于以上缺点,敏感信息必须从Session获取。
    在服务端为Session生成一个键值,将该键值写入Cookie,用户凭借键值获取自己的session 用于解决http协议无状态缺陷,维护用户状态。
     
    七、数据上传
    var hasBody = function(req){
        return 'transfer-encoding' in req.Headers || 'content-length' in req.Headers; 
    }
     
    function(req, res){
        if(hasBody(req)){
            var buffers = [];
            req.on('data', function(chunk){
                buffers.push(chunk);
            });
            req.on('end', function(){
                req.rawBody = Buffer.concat(buffers).toString();
                handle(req, res);
            });
        }else{
            handle(req, res);
        }
    };
    业务中可以判断conten-type 采用不同的方式解析req.rawBody
    x-www-form-urlencoded:
    req.body = querystring.parse(req.rawBody);
     
    var mime = function(req){
        return req.headers['content-type'].split(';')[0] || '';
    }
     
    var xml2js = require('xml2js');
    var handle = function(req, res){
        if(mime(req) === 'application/json'){
            try{
                req.body = JSON.parse(req.rowBody);
            }catch(){
                res.writeHeader('400');
                res.end('invalid json');
                return;
            }
        }
        if(mime(req) === 'application/xml'){
            xml2js.parseString(req,rawBody, function(err, xml){
                if(err){
                }else{
                    req.body = xml;
                }
            });
        }
    };
    附件上传:
    content-type : multipart/form-data
    var formidable = require('formidable');
    function(req, res){
        if(hasBody(req)){
            if(mime(req) === 'multipart/form-data'){
                var form = new formidable.IncomingForm();
                form.parse(req, function(err, fields, files){
                    req.body = fields;
                    req.files = files;
                    handle(req, res);
                });
            }
        }
    }
    八、数据上传与安全
    在解析表单,JSON和XML的过程中,如果用户提交的数据量较大,不能直接读入内存。避免内存耗尽的情况发生。一般解决这种问题有以下两种方案:
    (1)、限制上传内容的大小,超过大小直接返回400状态码
    (2)、流式解析,将数据导向磁盘中,Node中只保留文件路径。
    如下为Connect中采用的上传数据量的限制方式:
    var bytes = 1024;
     
    function(req, res){
        var received = 0;
        var len = req.headers['content-length']?parseInt(req.headers['content-length'], 10):null;
        if(len > bytes){
            res.writeHeader(413);
            res.end;
            return;
        }
     
        res.on('data', function(chunk){
            received += chunk.length;
            if(received > bytes){
                req.destroy();
            }
        });
        
        handle(req, res);
    };
     
    CSRF 跨站消息伪造
    防御方法:服务端生成随机数,将该随机数告知前端,前端在请求中携带该随机数。没有携带制定规则随机数的请求服务端一律不处理。
     
    九、附件下载
    设置响应头Content-Disposition : attachment;filename="fdfd.txt"
     
  • 相关阅读:
    各种类型的Dialog
    短信验证码的使用
    监听开机广播
    实现点击两次返回键退出
    Android 遮罩层效果--制作圆形头像
    Native方法的使用
    如何给数字添加分隔符
    自定义Toast
    Android px、dp、sp之间相互转换
    android:scrollbarStyle属性及滚动条和分割线覆盖问题
  • 原文地址:https://www.cnblogs.com/hellohuman/p/3891038.html
Copyright © 2020-2023  润新知