2014-02-03 3 views
1

На каждой странице моих сайтов я использую AJAX для опроса сервера и получения списка сообщений. Сервер поддерживает список сообщений и SessionId (я в среде ASP.NET, но я чувствую, что этот вопрос применим к любой технологии на стороне сервера), для которой предназначено сообщение. Если для конкретного SessionId найдено сообщение, оно возвращается скрипту на стороне клиента. Я использую библиотеку JavaScript для создания уведомления (используя noty, JQuery Notification Plugin). Когда он возвращает определенное сообщение, сервер отбрасывает это сообщение.Обнаружение уникальных вкладок браузера

Это хорошо работает, если у пользователя есть только одна вкладка/окно, открытое для определенного сайта. Однако предположим, что у них есть два открытых файла, и они делают что-то, что вызывает создание предупреждающего сообщения. У меня нет контроля над тем, на какую вкладку отправлено уведомление, поэтому пользователь может не увидеть предупреждающее сообщение.

Есть ли способ уникальной идентификации вкладки браузера? Тогда я мог бы передать это как один из параметров в моем вызове AJAX.

+1

window.MYID = Math.random(); ... doServerStuff (MYID, ...); ... if (response.id == MYID) ... вы можете использовать window.name для сохранения идентификатора в обновлении. – dandavis

+0

@ dandavis Будет ли это длиться через несколько запросов? Предположим, я установил window.MYID в Default.aspx, а затем пользователь переходит к MyAccount.aspx. Будет ли window.MYID по-прежнему инициализироваться с тем же значением? – mason

+0

window.MYID не будет, но window.name будет. Вы также можете использовать sessionStorage, если он доступен. – dandavis

ответ

0

Вы можете добавить параметр строки запроса tabId и управлять привязкой к вкладке с помощью javascript.

Существует функциональный прототип ниже:

$(function() { 

    // from: https://developer.mozilla.org/en-US/docs/Web/API/Window.location 
    function getQueryStringParameter (sVar) { 
    return decodeURI(window.location.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(sVar).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); 
    } 

    // from: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 
    function newGuid() { 
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 
     var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 
     return v.toString(16); 
    }); 
    } 

    window.tabId = getQueryStringParameter('tabId'); 

    // tabId not defined, reload the page setting it 
    if (!window.tabId) { 
    window.tabId = newGuid(); 
    } 

    // on click set the tabId of each link to current page 
    $(document).on('click', 'a', function (e) { 
    var $this = $(this); 
    var newLocation = $(this).attr("href"); 

    // In page links 
    if (newLocation.match(/^#.+$/)) { 
     return; 
    } 

    // Outbound links 
    if (newLocation.match(new RegExp("^https?")) && !newLocation.match(new RegExp("^https?://" + window.location.host))) { 
     return; 
    } 

    // Insert tab id 
    if (newLocation.match(/(\?|&)tabId=[0-9a-f-]+/)) { 
     newLocation.replace(/(\?|&)tabId=[0-9a-f-]+/, (newLocation.indexOf('?') == -1 ? "?" : "&") + "tabId=" + window.tabId); 
    } else { 
     newLocation += (newLocation.indexOf('?') == -1 ? "?" : "&") + "tabId=" + window.tabId; 
    } 

    window.location.href = newLocation; 
    e.preventDefault(); 
    }); 
}); 

Если вы попадете на страницу в приложении без установки параметра на строку запроса tabId, он будет установлен на новый UUID (Guid).

Когда на странице есть параметр tabId строки запроса, она определяет переменную window.tabId внутри вашей страницы, и вы можете использовать ее в своем приложении.

Когда пользователь нажимает на любую ссылку на вашей странице, будет запущено событие javascript, и URL-адрес ссылки будет перенаправлен в соответствии с текущим tabId. Щелчок правой кнопкой мыши для открытия на новой вкладке или средним щелчком не инициирует это событие, и пользователь будет отправлен на новую вкладку без параметров строки запроса, поэтому новая страница создаст новый tabId на этой странице.

Вы можете увидеть здесь работать: http://codepen.io/anon/pen/sCcvK

+0

Мне нравится, где вы собираетесь с этим.Мне не нравится использовать строку запроса, чтобы передать ее, но я думаю, что это хорошее решение для серверов-агностиков. – mason

+0

В зависимости от технологии, используемой на стороне сервера, она может быть частью маршрута, в ASP .net MVC может быть, например, '/ {tabId}/{controller}/{action}/{id}'. – LawfulHacker

+0

Ну, это просто избежать проблемы. Он по-прежнему находится в URL-адресе. В идеале это не было бы ни на чем видимом пользователем, кроме источника HTML. – mason

1

Во-первых, опрос не кажется, хороший механизм. Это может поразить ваш сервер, когда у вас много активных пользователей. В идеале вы должны вернуть сообщение в ответ на запрос, который был результатом недействительного действия.

Еще ниже решение может сработать для вас. Это вдохновляет ответ @SergioGarcia.

Храните скрытый ввод непосредственно перед тегом формы, в котором хранится уникальный идентификатор для однозначной идентификации вкладки. Вы хранить сообщения на сервере сессии против уникального табетический,

<input type="hidden" id="hiddenInputTabId" value="<%=getValue()%>" /> 

, а затем определить getValue.

function string getValue() { 
    var v = getValueFormBodyOrAccessValueDirectlyByMakingInput_a_ServerSideControl(); 
    if (string.IsNullOrWhiteSpace(v)) { 
     return Guid.NewId(); 
    } else { 
     return v; 
    } 
} 

Поскольку это скрытый вход, вы должны получить его значение в посланной форме тела, а также для Ajax запросов ниже фрагмента кода должны заботиться о отправках этого значения в заголовке которых you can access on server side.

$.ajaxSetup({ 
    beforeSend: function(xhr) { 
     xhr.setRequestHeader("tabId", $('#hiddenInputTabId').val()); 
    }, 
}); 

же заголовок можно проверить при возвращении ответа на запросы опроса и отвечать только сообщение, против предоставленному табетический должен быть дан ответ.

+0

Согласен, опрос - это не очень хорошая идея. Я рассматриваю использование SignalR в качестве альтернативы. – mason

0

Вы можете сделать это, создав уникальный идентификатор вкладки с javascript, загрузив своего клиента.

Я настоятельно рекомендую вам использовать что-то для обмена intertab, например intercom.js, которое может транслировать сообщения с одной вкладки с одним подключением к каждой другой вкладке. Intertab работает с socket.io, который имеет длительный откат, поэтому он может быть хорошим и в вашей текущей системе. Я согласен с тем, что опрос является плохим выбором, и вместо этого вы должны использовать websockets.

Если вы используете ZMQ на сервере, тогда в браузере вы можете использовать NullMQ либо (для websockets ofc). Я думаю, что у него нет поддержки intertab, поэтому вы должны сделать свое собственное решение intertab, чтобы оно работало. Не так сложно писать такую ​​систему, вам нужно только общее хранилище, например localStorage, но это может быть даже cookie ... Если у вас нет события хранения, вам нужно выполнить ping для хранения с помощью изменений setInterval. Вы должны хранить там сообщения, а какая вкладка транслирует их (возможно, в семафоре), и когда он последний раз пингал хранилище. После этого вы можете синхронизировать каждую вкладку с другими или использовать уникальный идентификатор вкладки, вы можете отправлять настроенные сообщения на любую вкладку. Если вкладка широковещательной рассылки имеет тайм-аут хранения (она долгое время не проверила хранилище), то она, вероятно, закрыта, поэтому вы должны назначить услугу трансляции другой вкладке.

0

Итак, что я в конечном итоге делал, менялось, как моя система уведомлений функционировала, чтобы предотвратить необходимость идентификации уникальных вкладок. Слишком сложно сложить информацию о безстоящей сети.

Вместо того, чтобы использовать Ajax для мгновенного вывода сообщений клиенту, я создаю их на каждой странице в свойстве List<Message>. PreRender Я предоставляю их клиенту с ClientScript.RegisterStartupScript(). Но если мне нужно отправить пользователя на другую страницу, я вместо этого перейду на Server.Transfer() вместо Response.Redirect(), чтобы сохранить очередь сообщений. Новая страница проверяет старую страницу, чтобы узнать, существует ли она, и если это правильный тип. Если это правильный тип, я бросаю его и получаю очередь сообщений со старой страницы и добавляю их в очередь новой страницы. И поскольку Server.Transfer() не обновляет URL-адрес на клиенте, я также добавил функцию JavaScript, чтобы вручную указать состояние URL-адреса в поддерживаемых браузерах.

Я знаю, что я принял это в немного другом направлении, чем я сделал по этому вопросу, но я думаю, что в начале я ошибался.

Смежные вопросы