web-dev-qa-db-fra.com

Demander au navigateur de soumettre un en-tête HTTP supplémentaire si vous cliquez sur un lien hypertexte

Existe-t-il un moyen de faire en sorte que le navigateur Web envoie un en-tête HTTP supplémentaire si l'utilisateur clique sur un lien?

Contexte: Dans notre environnement, chaque requête http a un identifiant unique côté serveur. Voir https://serverfault.com/questions/797609/Apache-x-request-id-like-in-heroku

Si votre application Web reçoit une requête http, j'aimerais savoir quelle page était la page précédente. Le référent http ne suffit pas, car l'utilisateur peut utiliser plusieurs onglets dans son navigateur.

Je voudrais éviter de mettre l'id de demande-laid dans chaque demande GET qui est envoyée par le navigateur au serveur. Jusqu'à présent, nos URL sont Nice.

Ma solution préférée serait une certaine magie JavaScript qui ajoute l'identifiant de la demande à la page actuelle dans la prochaine requête http.

Étapes en détail:

  1. uRL d'accès au navigateur http://example.com/search
  2. le serveur Web reçoit la demande http avec l'ID de demande 123
  3. le serveur Web envoie le contenu de l'URL au navigateur (une page de recherche). La page inclut l'ID de demande 123 quelque part
  4. l'utilisateur recherche "foobar". 
  5. le navigateur Web envoie une demande http au serveur et inclut d'une manière ou d'une autre l'identifiant de demande précédent.
  6. le serveur Web reçoit la deuxième demande http (ID 456) et peut accéder à la valeur de la première demande (ID 123) d'une manière ou d'une autre.
  7. Le serveur Web peut stocker la relation "123 -> 456" dans une base de données pour une analyse ultérieure.

Mon objectif est de suivre les relations "123 -> 456". La solution ci-dessus n’est qu’une stratégie pour atteindre l’objectif. D'autres stratégies sont les bienvenues.

Nous utilisons le framework web Django. Mais autant que je sache, cela compte dans ce contexte.

l'utilisateur peut utiliser plusieurs onglets dans son navigateur

Je précise ce que cela signifie pour une solution correspondante. La séquence de demandes émanant d’un utilisateur ne résout pas le problème. 

Une utilisation avec plusieurs onglets:

  1. l'utilisateur regarde la page A dans l'onglet 1
  2. l'utilisateur regarde la page B de l'onglet 2
  3. l'utilisateur suit un lien de la page A à la page C
  4. l'utilisateur suit un lien de la page C à la page D
  5. l’utilisateur suit un lien de la page B (onglet 2) vers la page E.

Je veux savoir voir deux séquences:

A -> C -> D

Et

B -> E
10
guettli

La seule option «rationnelle» moderne consiste ici à utiliser un ServiceWorker.

Un ServiceWorker peut intercepter les demandes HTTP d'un domaine que vous contrôlez et le décorer avec plusieurs en-têtes.

Un ServiceWorker fonctionne "en dehors" d'un onglet de navigateur, et si plusieurs onglets sont ouverts avec le même site Web, le même serviceworker sera utilisé pour tous.

Un didacticiel complet sur la façon de procéder est certainement trop compliqué pour cette boîte de réponse, mais intercepter et traiter des requêtes HTTP est un cas d'utilisation très volumineux. Par conséquent, les sources hors site le prennent généralement pour exemple.

Je dirais que c'est un peu une mauvaise idée. Si vous pensez en avoir besoin, vous pouvez peut-être gérer cela différemment. Une méthode courante consiste à utiliser des cookies. 

8
Evert

Tout d'abord, désolé pour mon anglais.

Modifier:

Après avoir lu votre édition, je me suis rendu compte que ma réponse ne correspondait pas du tout, à cause des onglets.

Il n’est pas possible de modifier directement la manière dont le navigateur fait une demande d’obtention. Sachant cela, vos possibilités sont:

  • Utilisez les paramètres GET. Je sais que vous essayez d'éviter cela.
  • Comme @Evert dit, utilisez ServiceWorkers. C'est le moyen le plus propre de modifier une demande avant qu'elle ne quitte le navigateur.
  • La dernière approche (une solution facile) est similaire à celle de @ Emeeus, mais au lieu d'utiliser localStorage, dont les valeurs sont partagées entre les onglets, vous devez utiliser sessionStorage, dont les valeurs sont indépendantes de tabulation . En outre, au lieu de stocker la totalité de l'itinéraire, vous devez stocker uniquement un ID aléatoire. Cet identifiant fonctionnera comme l'identification de la chaîne de demandes pour un onglet spécifique. Ensuite, une fois que votre serveur Web renvoie chaque Request-ID, par exemple en utilisant <meta name="request_id" content="123" />, il vous suffit de faire une demande via ajax à un point de terminaison de suivi spécifique et de stocker:
    • chain_id (stocké dans sessionStorage)
    • request_id (stocké dans head> meta)
    • timestamp (généré sur le serveur Web)
    • session_id (accessible depuis le serveur Web). Vous pouvez éviter cela, mais cela reste utile pour vérifier.

La demande de stockage de l'itinéraire est faite après le chargement de votre page, au lieu d'avant. Cette approche est assez similaire à la façon dont Analytics fonctionne.

// generate an unique code and store it in sessionStorage.
if (!sessionStorage.getItem('chain_id')) { 
    sessionStorage.setItem('chain_id', 'a7835e0a-3ee9-e981-...');
}

// Then, if you use JQuery:
$(document).ready(function() {
    $.ajax({
        type: "POST",
        url: 'your/tracking/endpoint/',
        data: {
            'chain_id': sessionStorage.getItem('chain_id'),
            'request_id': document.querySelector("meta[name='request_id']").getAttribute('content'),
        }
    });
});

Remarque: Il est préférable de ne pas utiliser JQuery pour traiter les demandes de suivi, ni d'attendre que le document soit complètement chargé. C'est juste un exemple.

Et c'est tout. Vous avez la relation entre l'agent utilisateur, la chaîne, la requête et l'horodatage de la requête. Par conséquent, si vous avez besoin de savoir quelle requête a été faite avant ou après une requête donnée, il vous suffit de rechercher dans la base de données à l'aide de la commande Chain-ID et l'horodatage en tant que filtres.

Le modèle Django pour vos demandes pourrait être.

from Django.db import models
from Django.contrib.sessions.models import Session


class Request(models.Model):
    session = models.ForeignKey(Session)
    chain_id = models.Charfield(max_length=100)
    request_id = models.WhatEverField...
    request_url = models.URLField(max_length=200)
    created = models.DateTimeField(auto_now_add=True)

J'espère que ça aide.

1
ddiazp

Nous pouvons modifier les en-têtes de requête en utilisant: 

  • _ { .setRequestHeader () } méthode de l'objet XMLHttpRequest () (même ou origines autorisées ). 
  • Éditer les en-têtes dans la console du navigateur ou utiliser un complément (ce n’est pas pratique).
  • Exécuter la requête du côté serveur, par exemple en utilisant CURL, wget ou une bibliothèque (client-> serverProxy-> url avec des en-têtes personnalisés).

Il n'est pas possible (à l'aide de javascript) de modifier les en-têtes envoyés par le navigateur dans une requête telle que <a href=""></a> car, à tout le moins, la négociation de contenu http est une fonctionnalité interne du navigateur (sauf l'utilisation partielle de XMLHttpRequest dans same ou origines autorisées ).

Ensuite, à mon avis, comme @Evert l’a dit, vous disposez de deux méthodes pratiques (la troisième en fait) pour atteindre votre objectif, effectuer un proxy de serveur ou utiliser des cookies. Ici vous avez un moyen très simple d’utiliser window.localStorage } _:

Exemple de LocalStorage

if (!localStorage.getItem("ids")) {//<-- the place in which we store the behavior
  localStorage.setItem("ids", 'somevalue')
} else {
  var ids = JSON.parse(localStorage.getItem("ids"));
  ids.ids.Push(id);//<-- we add some value
  localStorage.setItem("ids", JSON.stringify(ids));  
}

Exemple complet ici: https://jsfiddle.net/hy4rzob9/ appuyez plusieurs fois et vous verrez que nous enregistrons chaque visite, bien sûr, dans votre implémentation, vous devez remplacer le nombre aléatoire par un nombre aléatoire. identifiant unique de chaque page. 

Exemple LocalStorage avec plusieurs onglets

En prenant en compte la mise à jour, nous pourrions stocker l’historique en utilisant aussi document.referrer avec localStorage avec quelque chose comme ceci:

var session = Math.random();

if(!localStorage.getItem("routes")){//<-- first time
    var routes = {};
routes[session] = [document.location.href];

localStorage.setItem("routes", JSON.stringify(routes))

}else{

    var routes = JSON.parse(localStorage.getItem("routes"));

    if(!document.referrer){
        routes[session] = [document.location.href];//<-- new root
    }else{

        for(let ses in routes){
             if(routes[ses].includes(document.referrer)){
                routes[ses].Push(document.location.href); 
             }
        }
    }

    localStorage.setItem("routes", JSON.stringify(routes))


}

var r = JSON.parse(localStorage.getItem("routes"));

console.log(r);

Exemple complet ici (https://codesandbox.io/s/qk99o4vy7q }, pour émuler votre exemple, ouvrez cette https://qk99o4vy7q.codesandbox.io/a.html } _ (représente A) et ouvrez un nouvel onglet https://qk99o4vy7q.codesandbox.io/b.html (représente B), naviguez dans les deux onglets et voyez la console. Cet exemple ne fonctionnera pas si nous partageons un référent, car nous ne pouvons pas différencier les référents si nous n'attachons rien dans l'URL. A -> C -> D et B -> E fonctionneront, mais A -> C -> D et B -> E -> A ne fonctionneront pas.

Exemple de ping

Il y a un autre moyen, facile mais avec une limitation de compatibilité du navigateur , qui utilise l'attribut ping de <a> comme ceci:

<a href="https://www.google.com/" ping="trackPing.py">Link to track</a>

ping Contient une liste d'URL séparées par des espaces auxquelles, lorsque lien hypertexte est suivi, les demandes POST avec le corps PING seront envoyées par le navigateur (en arrière-plan). Généralement utilisé pour le suivi.

Ouvrez la console -> réseau, supprimez tout, lancez l'extrait de code et cliquez sur le lien. Si votre navigateur le prend en charge, vous verrez qu'il envoie une demande POST à trackPing.py (je suppose qu'il n'existe pas dans SO. ), cet article est annulé, mais vous pouvez suivre les variables environnementales telles que request.environ['REMOTE_ADDR'] ou quelque chose du genre. 

1
Emeeus

Je ne sais pas si cela aidera, mais je pense que peut-être Ajax fera: Comme définir un en-tête supplémentaire dans l'écouteur d'événement onclick, comme pour l'id de la demande, si ce n'est pas quelque chose de si sensible, vous pouvez utiliser un cookie pour le conteneur, ou peut-être quelque chose de beaucoup mieux ...

0
Dhiva Banyu Wigara