我有一个骨干应用程序,我正在使用dualStorage并实现我自己的Backbone同步。我已经实现了自己的同步,因为在我的API中,它需要在每个请求的标头中发送身份验证令牌。如果此身份验证令牌不存在或无效,则API将返回401错误。
我的应用程序有两个选项卡,当您单击其中一个或它将路径从/ #cartlist切换到/#ticketlist时,反之亦然。此问题仅在切换选项卡时发生,而不是在转到应用程序中的任何其他路由时发生。这个问题让我感到非常奇怪,只有这两个请求失败,所有同步操作都应该被覆盖。
我所拥有的问题似乎只存在于Safari中,并且不存在于Chrome或Firefox中,但由于这主要在iPad上运行,因此我不能忽视此问题。
这是手头的问题
1。)登录系统,一切运行良好UI从API数据中填充 2.)单击“票证列表”选项卡,系统会将您注销。这是因为API返回401,因为请求中不存在身份验证令牌(仅在safari中)
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (ticketlist, line 0)
以下是我的Backbone.sync代码。
/*
* Store a version of Backbone.sync to call from the
* modified version we create
*/
var _nativeSync = Backbone.sync;
Backbone.sync = function (method, model, options) {
/*
* The jQuery `ajax` method includes a 'headers' option
* which lets you set any headers you like
*/
if(CheckinApp.getSession().isAuthenticated() !== false) {
/*
* Set the 'Authorization' header and get the access
* token from the `auth` module
*/
options.headers = {
'Authorization': 'Token ' + CheckinApp.getSession().getAuthorizationToken()
}
}
/*
* Call the stored original Backbone.sync method with
* extra headers argument added
*/
_nativeSync(method, model, options);
};
我唯一担心的是,这可能会因使用dualStorage而产生冲突,因为我知道它也会覆盖Backbone.sync方法。为了使这个工作,我必须在dualStorage之后包括我的同步,如下所示。
<script type="text/javascript" src="js/vendor/backbone.dualstorage.min.js"></script>
<script type="text/javascript" src="js/plugins/backbone.sync.js"></script>
我还在API端转储了请求的标头,并且可以看到这个特定请求在从Safari制作时没有授权令牌,但是在chrome或firefox的同一请求中没有。
[Tue Apr 07 14:49:08.677473 2015] [:error] [pid 16743] [client 71.181.125.154:64016] <pre>Array\n(\n
[Host] => jcrawford.heytix.com\n
[User-Agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/6.1.6 Safari/537.78.2\n
[Accept] => application/json, text/javascript, */*; q=0.01\n [
Referer] => http://jcrawford.heytix.com/guestlist/\n
[X-Requested-With] => XMLHttpRequest\n
[Authorization] => Token 951ba59c833a80e4ddaf72ee6b3d9143\n
[Accept-Language] => en-us\n [Accept-Encoding] => gzip, deflate\n
[Cookie] => 'removed from output'
[Connection] => keep-alive\n)\n</pre>, referer: http://jcrawford.heytix.com/guestlist/
[Tue Apr 07 14:49:12.027279 2015] [:error] [pid 16743] [client 71.181.125.154:64016] <pre>Array\n(\n
[Host] => jcrawford.heytix.com\n
[User-Agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/6.1.6 Safari/537.78.2\n
[Accept] => application/json, text/javascript, */*; q=0.01\n
[Referer] => http://jcrawford.heytix.com/guestlist/\n
[Accept-Encoding] => gzip, deflate\n
[X-Requested-With] => XMLHttpRequest\n
[Accept-Language] => en-us\n
[Cookie] => 'removed from output'
[Connection] => keep-alive\n)\n</pre>, referer: http://jcrawford.heytix.com/guestlist/
[Tue Apr 07 14:49:12.027565 2015] [:error] [pid 16743] [client 71.181.125.154:64016] HTTP 401 (GET /api/events/13044/guestlist), referer: http://jcrawford.heytix.com/guestlist/
这就是我用chrome和firefox获得的。
[Tue Apr 07 14:57:38.686859 2015] [:error] [pid 17630] [client 71.181.125.154:65109] <pre>Array\n(
[Host] => jcrawford.heytix.com
[Connection] => keep-alive
[Cache-Control] => max-age=0
[Accept] => application/json, text/javascript, */*; q=0.01
[X-Requested-With] => XMLHttpRequest
[User-Agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36
[Authorization] => Token 951ba59c833a80e4ddaf72ee6b3d9143
[Referer] => http://jcrawford.heytix.com/guestlist/
[Accept-Encoding] => gzip, deflate, sdch
[Accept-Language] => en-US,en;q=0.8
[Cookie] => 'removed from output'
)</pre>, referer: http://jcrawford.heytix.com/guestlist/
[Tue Apr 07 14:57:44.001465 2015] [:error] [pid 17492] [client 71.181.125.154:65106] <pre>Array\n(\n
[Host] => jcrawford.heytix.com\n
[Connection] => keep-alive\n
[Accept] => application/json, text/javascript, */*; q=0.01\n
[X-Requested-With] => XMLHttpRequest\n
[User-Agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36\n
[Authorization] => Token 951ba59c833a80e4ddaf72ee6b3d9143\n
[Referer] => http://jcrawford.heytix.com/guestlist/\n
[Accept-Encoding] => gzip, deflate, sdch\n
[Accept-Language] => en-US,en;q=0.8\n
[Cookie] => 'removed from output'
)\n</pre>, referer: http://jcrawford.heytix.com/guestlist/
从上面的日志记录中可以看出,授权令牌是通过Firefox / Chrome传递的,但不是通过Safari传递的。我已经在同步方法中添加了日志记录,并且在查询API之前在控制台中指出用户已经过身份验证,然后重定向到登录页面。
[Log] sync called, isAuthenticated: true (backbone.sync.js, line 12)
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (ticketlist, line 0)
我没有在Safari开发人员工具/控制台等中看到任何其他错误或任何内容。应用程序向API发出请求,获得401(正如预期的那样没有令牌),然后将用户从Backbone中注销应用程序并重定向到登录页面。当没有令牌存在时,这种行为是预期的。问题是为什么这不会为这些特定路由传递令牌?只有这些路由导致问题,所有其他路线似乎在UI中工作得很好。
我也将提供我的路由器,以便您可以看到正在发生的事情,请记住我正在使用事件来进行大部分实际路由,所以如果您需要查看任何其他代码,请告诉我
CheckinApp.Routers.Default = Backbone.Router.extend({
view: null,
public_routes: ['login'],
routes:{
"":"eventlist",
"login": "login",
"guestlist": "guestlist",
"ticketlist":"ticketlist",
"managerslist":"managerslist",
"events": "eventlist",
"organize(/)(:action)": "displayOrganize",
"eventreport(/)(:event_id)": "eventreport",
"venuereport(/)(:venue_name)": "venuereport"
},
initialize:function (options) {
this.view = options.view;
Backbone.history.start();
},
guestlist:function () {
CheckinApp.getVent().trigger('main:renderListView', {title: 'Guest List', type: 'ticket', tab_hash: '#guestlist'});
},
ticketlist:function () {
CheckinApp.getVent().trigger('main:renderListView', {title: 'Ticket List', type: 'ticket', tab_hash: '#ticketlist'});
},
managerslist:function () {
CheckinApp.getVent().trigger('main:renderListView', {title: 'Managers List', type: 'ticket', tab_hash: '#managerslist'});
},
eventlist: function() {
var vent = CheckinApp.getVent();
vent.trigger('main:renderListView', {title: 'Todays Events', type: 'event'});
vent.trigger('tabs:remove');
},
eventreport: function() {
var collection = new CheckinApp.Collections.EventReport({"event_id": 13044});
var view = new CheckinApp.Views.EventReport({collection: collection});
view.render();
},
venuereport: function(venue_name) {
var collection = new CheckinApp.Collections.VenueReport([], {"venue_name": 'borgata'});
var view = new CheckinApp.Views.VenueReport({collection: collection});
var modal = new Backbone.BootstrapModal({
content: view,
title: ' ',
animate: true
});
modal.open();
//view.render();
},
login: function() {
var view = new CheckinApp.Views.Login({});
view.render();
},
before: function (route, params) {
if($.cookie('CheckinApp') && CheckinApp.getSession().isAuthenticated() == false) {
CheckinApp.setSessionFromCookie(JSON.parse($.cookie('CheckinApp')));
}
var hasAccess = CheckinApp.getSession().isAuthenticated(); // If cookie exists they are logged in..
if (!hasAccess) {
this.navigate('login', true);
} else {
if(route == 'login') {
this.navigate('', true);
return false;
}
}
if((_.contains(this.public_routes, route) === false)) {
return hasAccess; //return true if you want to proceed to routes else return false
}
},
after: function(route, params) {
if(route == 'logout') return false;
else {
CheckinApp.updateCookie();
return true;
}
}
});
最后,这里的代码告诉jQuery监听401并在用户出现时注销。
$.ajaxSetup({
statusCode: {
401: function () {
CheckinApp.clearSession();
Backbone.history.navigate('#login', true);
}
}
});
我还更进了一步,在前面的路由中添加了一堆console.log语句,并提出了这个问题。似乎可能正在使用这些特定路由进行某些操作导致身份验证丢失?
[Log] sync : isAuthenticated = true (backbone.sync.js, line 12)
[Log] sync: url = http://jcrawford.heytix.com/guestlist/checkin/api/events/13044/guestlist/ (backbone.sync.js, line 13)
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (guestlist, line 0)
[Log] before : isAuthenticated: false (default.js, line 60)
[Log] cookie: undefined (default.js, line 61)
[Log] before : hasAccess = false (default.js, line 66)
[Log] before : hasAccess = false, going to login page (default.js, line 68)
[Log] before : going to route login (default.js, line 76)
正如您所看到的那样,在对数据进行AJAX请求的行之前,它会显示Authenticated,然后请求失败并且表示未经过身份验证。
这次从我的同步方法
进行更多记录sync : isAuthenticated = true (backbone.sync.js, line 12)
sync: url = http://jcrawford.heytix.com/guestlist/checkin/api/events/13044/guestlist/ (backbone.sync.js, line 13)
User is Authenticated (backbone.sync.js, line 15)
options: {"parse":true,"headers":{"Authorization":"Token 951ba59c833a80e4ddaf72ee6b3d9143"}} (backbone.sync.js, line 24)
正如您在上面所看到的那样,选项是在标题上设置的,但出于某种原因,使用Safari Backbone时不会使用同步请求发送这些标题。
根据一些建议(以及下面的建议),我试图修改jQuery的$ .ajaxSetup但是我收到的结果与我目前遇到的完全相同。
$.ajaxSetup({
headers: function() {
var token = '';
if(CheckinApp) {
var session = CheckinApp.getSession();
if(session) {
token = CheckinApp.getSession().getAuthorizationToken();
}
}
return {
"Authorization": "Token " + token
};
},
statusCode: {
401: function () {
CheckinApp.clearSession();
Backbone.history.navigate('#login', true);
}
}
});
非常感谢任何协助。
答案 0 :(得分:1)
此问题发生在SOLELY,因为我的Tickets Collection网址包含一个尾随/
随着尾随/到位,Safari会向带有斜杠的URL发送飞行前请求并收到302 Found。然后,它将向URI发出请求而不使用尾部斜杠,并接收第二个请求的401,而不是将授权令牌传递给API。
我不太确定这是否是Backbone或jQuery的问题,但无论哪一个似乎都不喜欢拖尾斜杠,因为删除它解决了Safari问题。
答案 1 :(得分:0)
您是否尝试直接使用$.ajaxSetup()
令牌并查看是否可以使用safari复制相同的问题?您的示例不确定如何在您的路由器中调用before
和after
函数,但看起来您可以设置ajax设置,因为您已经检查了hasAccess()。
示例:
$.ajaxSetup({
headers: { 'Authorization' : 'Token ' + howeverYouGetToken() }
});