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是無法使用的');
})
.on('data', function (err, data) {
. 🎜> })
.on('end', function (err, data) {
console.log('請求結束');
})
TP 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;
callback && this.emitter.on('check', callback);
}
Valid.prototype = {
コンストラクター: 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} イベント
* @param {Function} コールバック
*/
on: function (event, callback) {
(event === 'data') && (this.fetch = true);
this.emitter.on(event, callback);
return this;
},
/**
* 破棄
*/
destroy: function () {
this. emitter.removeAllListeners();
this.url = unknown;
this.emitter = null;
this.fetch = unknown;
},
/**
* RemoveAllListeners
* @param
*/
removeAllListeners: function (event) {
イベント ?
this.emitter.removeAllListeners(event) :
this.emitter.removeAllListeners();
return this;
},
/**
* リスナー
* @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;
}
}
}
/**
* one
* @param {String} url
* @param {Function} コールバック
* @return {Valid}
*/
function one(url, callback) {
return (新しい有効(url, コールバック));
}
one.one = one;
return one;
})();