首页 / 脚本样式 / JavaScript / NodeJS url验证(url-valid)的使用方法
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;
})();