Marionette.AppRouter
は標準を拡張しますがBackbone.Router
、どちらもルート コールバックの実行を防止できるインターセプト メカニズムを提供しません。同じ機能が必要なため、次のコードを思いつきました ( _
is Lodash )。元のBackbone.Router.prototype.route()
では、ルート マッチで実行される関数が作成されます。Backbone.Router.prototype.route()
次のコードは、登録されたルート コールバックを一連のミドルウェア関数を実行する関数に置き換えるために、オリジナルのラッパーを提供します。これらの各ミドルウェアは、元のルート コールバックの実行前または実行後に追加の処理を実行できます。これには、さらなるコールバックの実行の防止や例外の発生が含まれます。
Backbone.RouteMiddlewares = (function () {
var Backbone = require('backbone')
, _ = require('lodash');
/**
* A wrapper for `Backbone.Router.prototype.route()` that will use route middlewares (in addition to its
* normal callback).
*
* @param {Function} fn - The wrapped function (should be compatible with `Backbone.Router.prototype.route()`).
* @param {string|RegExp} route - A route pattern or regexp.
* @param {string} name - The name of the route.
* @param {Function} callback - The route callback.
* @returns {Backbone.Router}
*/
function RouteMiddleware(fn, route, name, callback) {
// Prepare arguments as the original Backbone.Router.prototype.route() would.
if (!_.isRegExp(route)) route = Backbone.Router.prototype._routeToRegExp.apply(this, [route]);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
// Execute the wrapper `route` method, with the callback as final middleware.
return fn.apply(this, [route, name, function () {
executeMiddlewares.apply(this, [route, name, Backbone.history.getFragment(), Array.prototype.slice.apply(arguments, [0]), callback]);
}]);
};
/**
* Add a route middelware.
*
* @param {RouteMiddleware} fn
*/
RouteMiddleware.use = function use(fn) {
middlewares.push.apply(middlewares, _.filter(arguments, _.isFunction));
};
/**
* @callback RouteMiddleware
* @param {RegExp} route - The matched route regexp.
* @param {string} name - The matched route.
* @param {string} fragment - The matched URL fragment.
* @param {Array} args - The route arguments (extracted from `fragment`).
* @param {Function} next - The function to call to execute the next middleware in the chain.
*/
/**
* @type {RouteMiddleware[]}
*/
var middlewares = [];
/**
* Execute the route middlware, ending with the provided callback.
*
* @param {RegExp} route - The matched route regexp.
* @param {string} name - The matched route.
* @param {string} fragment - The matched URL fragment.
* @param {Array} args - The route arguments (extracted from `fragment`).
* @param {Function} callback - The route handler to execute as final middleware.
*/
function executeMiddlewares(route, name, fragment, args, callback) {
var index = 0;
var h = middlewares.concat(function (route, name, fragment, args, next) {
callback.apply(this, args);
});
function next(err) {
if (err) throw err;
h[index++].apply(this, [route, name, fragment, args, next.bind(this)]);
}
next.apply(this);
}
})();
Backbone.Router.prototype.route = _.wrap(Backbone.Router.prototype.route, Backbone.RouteMiddlewares);
このインターセプター/ミドルウェア メカニズムを使用すると、次の方法でユーザーをログイン ページにリダイレクトできます。
Backbone.RouteMiddlewares.use(function (route, name, fragment, args, next) {
if (App.routeRequiresAuthentication(name) && !App.isUserAuthenticated()) {
Backbone.history.navigate('login', {trigger: true});
}
else {
next();
}
});
注: ルート コールバックの後にミドルウェアを介してコードを実行できることはroute
、ルーターとオンのイベントで冗長ですがBackbone.history
、ミドルウェア パターンでは無料です。