NodeJS url验证(url-valid)的使用方法


Posted in NodeJs onNovember 18, 2013

Javascript做url检验,通常是使用正则表达式来判定,其格式是否正确,例如:

/^https?:\/\//.test(url);

当然还有更好的检测方法比如基于RFC 3986, RFC 3966, RFC 4694, RFC 4759, RFC 4904等标准的进行验证的valid-url库。
不过个根据格式进行验证当然不能确定该url是否存在啦,所以就有了url-valid,我们基于HTTP请求进行验证。

接口设计
实际上我们只需要一个函数传入一个url地址,并回调返回该链接是否可用。
但请求容易产生未知错误,所以我们在回调函数传入一个error参数,如果不为空,则有错误产生。
我们可能还希望能够得到网页的相关数据,未来用在页面的信息提取上。
尽可能链式操作吧。
所以最后使用上大概是这样的:

valid(url)
  .on('check', function (err, status) {
    if (err) throw err;
    status ?
      console.log('url是可用的') :
      console.log('url是不可用的');
  })
  .on('data', function (err, data) {
    console.log(data);
  })
  .on('end', function (err, data) {
    console.log('请求结束');
  })

HTTP GET 还是 HTTP HEAD
本来我们想利用HTTP HEAD请求来实现的,因为HEAD请求只会返回头信息,这可以减少请求时间,但是HEAD请求,不一定所有链接都会支持。
所以最后我们使用HTTP GET方式,在得到正确的statusCode后立刻abort掉请求。

处理301-303
因为301到303都是重定向状态所以,我们需要继续检查对应Location是否依然存在。

利用process.nextTick异步执行
为了在注册监听后,再执行代码,我们使用process.nextTick来一步操作。

实现

/*!
 * valid
 * MIT Licensed
 */
module.exports = (function () {
  'use strict';
  var http = require('http')
    , https = require('https')
    , EventEmitter = require('events').EventEmitter
    , URL = require('url')
    , urlReg = /^(https?):\/\//;  /**
   * Valid
   * @class
   */
  function Valid(url, callback) {
    var that = this;
    this.url = url;
    this.emitter = new EventEmitter();
    process.nextTick(function () {
      that.get(url);
    });
    this.fetch = false;
    callback && this.emitter.on('check', callback);
  }
  Valid.prototype = {
    constructor: Valid,
    /**
     * get
     * @param {String} url
     */
    get: function (url) {
      var match = url.match(urlReg)
        , that = this;
      if (match) {
        var httpLib = (match[1].toLowerCase() === 'http') ? http : https
          , opts = URL.parse(url)
          , req;
        opts.agent = false;
        opts.method = 'GET';
        req = httpLib.request(opts, function (res) {
          var statusCode = res.statusCode;
          if (statusCode === 200) {
            that.emitter.emit('check', null, true);
            that.fetch ? 
              (res.on('data', function (data) {
                that.emitter.emit('data', null, data);
              }) && res.on('end', function () {
                that.emitter.emit('end');
              })) :
              (req.abort() || that.emitter.emit('end'));
          } else if (300 < statusCode && statusCode < 304) {
            req.abort();
            var emitter = that.emitter
              , valid = one(URL.resolve(url, res.headers.location), function (err, valid) {
                emitter.emit('check', err, valid);
              });
            that.fetch && valid.on('data', function (err, data) {
              emitter.emit('data', err, data);
            });
            valid.on('error', function (err) {
              that.emitter.emit('error', err);
            });
            valid.on('end', function () {
              that.emitter.emit('end');
            });
          } else {
            that.emitter.emit('check', null, false);
          }
          res.on('error', function (err) {
            req.abort();
            that.emitter.emit('data', err);
          });
        });
        req.on('error', function (err) {
          req.abort();
          return that.emitter.emit('check', null, false);
        });
        req.end();
      } else {
        return that.emitter.emit('check', null, false);
      }
    },
    /**
     * on
     * @param {Stirng} event
     * @param {Function} callback
     */
    on: function (event, callback) {
      (event === 'data') && (this.fetch = true); 
      this.emitter.on(event, callback);
      return this;
    },
    /**
     * destroy
     */
    destroy: function () {
      this.emitter.removeAllListeners();
      this.url = undefined;
      this.emitter = null;
      this.fetch = undefined;
    },
    /**
     * removeAllListeners
     * @param
     */
    removeAllListeners: function (event) {
      event ? 
        this.emitter.removeAllListeners(event) :
        this.emitter.removeAllListeners();
      return this;
    },
    /**
     * listeners
     * @param
     */
    listeners: function (event) {
      if (event) {
        return this.emitter.listeners(event);
      } else {
        var res = []
          , that = this
          , _push = Array.prototype.push;
        Object.keys(this.emitter._events).forEach(function (key) {
          _push.apply(res, that.emitter.listeners(key));
        });
        return res;
      }
    }
  }
  /**
   * one
   * @param {String} url
   * @param {Function} callback
   * @return {Valid}
   */
  function one(url, callback) {
    return (new Valid(url, callback)); 
  }
  one.one = one;
  return one;
})();
NodeJs 相关文章推荐
基于NodeJS的前后端分离的思考与实践(二)模版探索
Sep 26 NodeJs
NodeJS中利用Promise来封装异步函数
Feb 25 NodeJs
基于html5和nodejs相结合实现websocket即使通讯
Nov 19 NodeJs
Nodejs中解决cluster模块的多进程如何共享数据问题
Nov 10 NodeJs
Nodejs进阶:基于express+multer的文件上传实例
Nov 21 NodeJs
nodejs根据ip数组在百度地图中进行定位
Mar 06 NodeJs
NodeJS测试框架mocha入门教程
Mar 28 NodeJs
nodejs服务搭建教程 nodejs访问本地站点文件
Apr 07 NodeJs
nodejs socket服务端和客户端简单通信功能
Sep 14 NodeJs
详解从NodeJS搭建中间层再谈前后端分离
Nov 13 NodeJs
NodeJS模块Buffer原理及使用方法解析
Nov 11 NodeJs
node快速搭建后台的实现步骤
Feb 18 NodeJs
NodeJS与Mysql的交互示例代码
Aug 18 #NodeJs
利用NodeJS的子进程(child_process)调用系统命令的方法分享
Jun 05 #NodeJs
将nodejs打包工具整合到鼠标右键的方法
May 11 #NodeJs
用nodejs写的一个简单项目打包工具
May 11 #NodeJs
nodejs教程 安装express及配置app.js文件的详细步骤
May 11 #NodeJs
nodejs中exports与module.exports的区别详细介绍
Jan 14 #NodeJs
nodejs的require模块(文件模块/核心模块)及路径介绍
Jan 14 #NodeJs
You might like
excellent!――ASCII Art(由目标图象生成ascii)
2007/02/20 PHP
解析php安全性问题中的:Null 字符问题
2013/06/21 PHP
浅析ThinkPHP的模板输出功能
2014/07/01 PHP
总结PHP内存释放以及垃圾回收
2018/03/29 PHP
php 的多进程操作实践案例分析
2020/02/28 PHP
JavaScript 动态将数字金额转化为中文大写金额
2009/05/14 Javascript
JQuery1.8 判断元素是否绑定事件的方法
2014/07/10 Javascript
浅谈javascript中的instanceof和typeof
2015/02/27 Javascript
关于微信中a链接无法跳转问题
2016/08/02 Javascript
用move.js库实现百叶窗特效
2017/02/08 Javascript
ES6中Proxy代理用法实例浅析
2017/04/06 Javascript
JS实现数组去重方法总结(六种方法)
2017/07/14 Javascript
详解用webpack把我们的业务模块分开打包的方法
2017/07/20 Javascript
vue+vue-validator 表单验证功能的实现代码
2017/11/13 Javascript
Vue.js最佳实践(五招助你成为vuejs大师)
2018/05/04 Javascript
Vue 配合eiement动态路由,权限验证的方法
2018/09/26 Javascript
vue打开其他项目页面并传入数据详解
2020/11/25 Vue.js
[56:12]LGD vs Optic Supermajor小组赛D组胜者组决赛 BO3 第一场 6.3
2018/06/04 DOTA
Python实现模拟登录及表单提交的方法
2015/07/25 Python
Python循环语句之break与continue的用法
2015/10/14 Python
Python requests发送post请求的一些疑点
2018/05/20 Python
Python实现读取SQLServer数据并插入到MongoDB数据库的方法示例
2018/06/09 Python
Ubuntu下Python2与Python3的共存问题
2018/10/31 Python
使用Python进行目录的对比方法
2018/11/01 Python
详解numpy.meshgrid()方法使用
2019/08/01 Python
python3的UnicodeDecodeError解决方法
2019/12/20 Python
关于Python3爬虫利器Appium的安装步骤
2020/07/29 Python
详解Python中的Lock和Rlock
2021/01/26 Python
简单介绍HTML5中的文件导入
2015/05/08 HTML / CSS
使用canvas生成含有微信头像的邀请海报没有微信头像问题
2019/10/29 HTML / CSS
外贸英语专业求职信范文
2013/12/25 职场文书
优秀志愿者事迹材料
2014/02/03 职场文书
课例研修方案
2014/05/31 职场文书
老公写给老婆的检讨书
2015/05/06 职场文书
毕业班班主任工作总结2015
2015/07/23 职场文书
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python