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来一步操作。
实现
/*!
* 有效
* MIT 授权
*/
module.exports = (function () {
'use strict';
var http = require('http')
, https = require('https')
, EventEmitter = require('events').EventEmitter
, URL = require('url')
, urlReg = /^(https?):///;
/**
* 有效
* @class
*/
function Valid(url, callback) {
var that = this;
this. url = url;
this.emitter = new EventEmitter();
process.nextTick(function () {
that.get(url);
});
this.fetch = false;
回调 && this.emitter.on('check', 回调);
}
Valid.prototype = {
构造函数: Valid,
/**
* 获取
* @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 req.abort();
varemitter = 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.发出('错误', 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} 事件
* @param {Function} 回调
*/
on: 函数(事件,回调){
(event === 'data') && (this.fetch = true);
this.emitter.on(事件, 回调);
return this;
},
/**
* 摧毁
*/
destroy: function () {
this. emitter.removeAllListeners();
this.url = undefined;
this.emitter = null;
this.fetch = undefined;
},
/**
* 移除所有监听器
* @param
*/
removeAllListeners:函数(事件){
事件?
this.emitter.removeAllListeners(event) :
this.emitter.removeAllListeners();
返回此;
},
/**
* 听众
* @param
*/
监听器: 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;
}
}
}
/**
* 一个
* @param {String} url
* @param {Function} 回调
* @return {Valid}
*/
函数一(url, 回调) {
return (新的有效(网址,回调));
}
one.one = 1;
返回 1;
})();