Javascript は通常、正規表現を使用して URL をチェックし、形式が正しいかどうかを判断します。例:
/^https?:///.test(url);
もちろん、RFC 3986、RFC 3966、 RFC 4694、RFC 4759、RFC 4904、および検証用のその他の標準有効 URL ライブラリ。
もちろん、形式に基づく検証では URL が存在するかどうかを判断できないため、url-valid では HTTP リクエストに基づいて検証します。
インターフェイス設計
実際に必要なのは、URL アドレスを渡す関数と、リンクが利用可能かどうかを返すコールバックだけです。
ただし、リクエストでは不明なエラーが発生する可能性があるため、コールバック関数にエラー パラメータを渡します。それが空でない場合は、エラーが発生します。
将来的には、Web ページの関連データを取得し、それを使用してページから情報を抽出することも考えられます。
可能な限り操作を連鎖させてください。
最終的な使用法はおそらく次のようになります:
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 を取得した直後にリクエストを中止します。
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} 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;
})();