web-dev-qa-db-fra.com

Quelle est la bonne façon de définir l'en-tête Location pour une réponse HTTP 201 dans une application Servlet Java

Considérez le code suivant envoyant une réponse HTTP 201 "Créé" au client:

    String url = "/app/things?id=42"; // example
    response.setStatus(HttpServletResponse.SC_CREATED);
    response.setContentType("text/plain");
    response.setHeader("Location", url);
    response.getWriter().print(url);

Il informe le client qu'une nouvelle "chose" a été créée et qu'elle peut être trouvée à l'URL /app/things?id=42. Le problème est que cette URL est relative. Ce serait parfait pour un JSP, qui pourrait s'écrire comme suit:

<img src="<c:url value="/things?id=42" />" />

Ce qui produirait le HTML suivant:

<img src="/app/things?id=42" />

C'est ce que nous voulons pour les applications Web.

Mais je ne pense pas que ce soit ce que nous voulons pour un en-tête d'emplacement de réponse 201. La spécification HTTP déclare :

14.30 Emplacement

Le champ d'en-tête de réponse d'emplacement est utilisé pour rediriger le destinataire vers un emplacement autre que l'URI de demande pour l'achèvement de la demande ou l'identification d'une nouvelle ressource. Pour 201 réponses (créées), l'emplacement est celui de la nouvelle ressource créée par la demande. Pour les réponses 3xx, l'emplacement DEVRAIT indiquer l'URI préféré du serveur pour la redirection automatique vers la ressource. La valeur du champ consiste en un seul URI absolu.

       Location = "Location" ":" absoluteURI

Un exemple est:

       Location: http://www.w3.org/pub/WWW/People.html

Ma question est de savoir comment traduire cette URL relative en URL abrégée pour l'en-tête Location de la bonne manière pour les servlets.

Je ne crois PAS qu'en utilisant:

request.getServerName() + ":" + request.getServerPort() + url;

Est la bonne solution. Il devrait y avoir une méthode standard qui produit la sortie correcte (afin que la réécriture d'URL, etc., puisse être appliquée). Je ne veux pas créer de hack.

34
les2

Envoyez simplement le chemin absolu. La restriction à un URI absolu est un défaut connu dans RFC 2616 et sera corrigé dans HTTPbis (voir http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185 ) .

Veuillez noter que la RFC 7231 inclut désormais les URI relatifs dans la spécification . Voir les autres réponses pour savoir comment gérer les relatifs URI.

22
Julian Reschke

Décidé de suivre les conseils de Julian Reschke et de violer les spécifications! Au moins, j'ai ajouté le commentaire suivant:

        /* Note: strictly speaking (per section 14.30 of RFC 2616), the Location header 
         * requires an *absolute URI*. However, in practice, many web 
         * applications send an *absolute path* instead. This is interoperable, 
         * that is, works in popular web browsers  (according to 
         * http://en.wikipedia.org/wiki/HTTP_location).
         * 
         * As the information required to set the Location header to an absolute URI
         * is not generally available to a Servlet, we go with the flow and send
         * an absolute path instead (in violation of RFC 2616).
         * 
         * There is an issue filed with Hypertext Transfer Protocol Bis (httpbis) 
         * working group to resolve this problem here:
         * http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185
         */
        response.setHeader("Location", url);

La raison pour laquelle je ne veux pas envoyer l'URI absolu moi-même, c'est parce que j'ai vu des problèmes avec cela lorsque derrière des équilibreurs de charge et d'autres infrastructures de production. Bien qu'en mode dev "http: // localhost: 8080/foo" a tendance à bien fonctionner :))

Acceptera la réponse de Julian maintenant ...

4
les2

Malheureusement, l'API servlet ne fournit pas de méthode qui renvoie directement l'URL absolue jusqu'à la racine de contexte. Pour cela, j'ai dû plusieurs fois utiliser une combinaison de getRequestURL() , getRequestURI() et getContextPath() .

String absoluteContextRootURL = request.getRequestURL().toString().replace(request.getRequestURI().substring(1), request.getContextPath());
4
BalusC

Si vous utilisez JAX RS, il existe une méthode dans javax.ws.rs.core.Response Qui convertit automatiquement les URL relatives :

public static Response.ResponseBuilder created(Java.net.URI location)

Créez un nouveau ResponseBuilder pour une ressource créée, définissez l'en-tête d'emplacement à l'aide de la valeur fournie.

Paramètres:

  • location - l'URI de la nouvelle ressource. Si un URI relatif est fourni, il sera converti en URI absolu en le résolvant par rapport à l'URI de demande.

Notez cependant qu'il y a un bogue dans l'implémentation JAX RS CXF qui conduit à des URL absolues incorrectes .

4
oberlies

Vous pourriez essayer

new URL(new URL(request.getRequestURL().toString()), url).toString();

Ce serait au moins judicieux de canoniser tout .. ou autres bizarreries. En dehors de cela, je ne pense pas que ce soit beaucoup mieux que la manipulation de chaînes.

2
Robert Tupelo-Schneck