web-dev-qa-db-fra.com

Différence entre / et / * dans le modèle d'URL de mappage de servlets

Le code familier:

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

D'après ce que je comprends, /* est mappé sur http://Host:port/context/*.

Qu'en est-il de /? Cela ne correspond certainement pas à http://Host:port/context racine uniquement. En fait, il acceptera http://Host:port/context/hello, mais rejetera http://Host:port/context/hello.jsp.

Quelqu'un peut-il expliquer comment est mappé http://Host:port/context/hello?

170
Candy Chiu

_<url-pattern>/*</url-pattern>_

_/*_ sur un servlet remplace tous les autres servlets, y compris tous les servlets fournis par le servletcontainer, tels que le servlet par défaut et le servlet JSP. Quelle que soit votre requête, elle se retrouvera dans cette servlet. C'est donc un mauvais modèle d'URL pour les servlets. Généralement, vous souhaitez utiliser _/*_ sur un Filter uniquement. Il est possible de laisser la requête se poursuivre vers l'un des servlets écoutant un modèle d'URL plus spécifique en appelant FilterChain#doFilter() .

_<url-pattern>/</url-pattern>_

_/_ ne remplace aucun autre servlet. Il remplace uniquement le servlet par défaut intégré du servletcontainer pour toutes les demandes qui ne correspond à aucun autre servlet enregistré. Ceci n'est normalement appelé que sur des ressources statiques (CSS/JS/image/etc) et des listes de répertoires. Le servlet par défaut intégré à servletcontainer est également capable de traiter les demandes de cache HTTP, la diffusion multimédia en continu (audio/vidéo) et la reprise de téléchargement de fichier. En règle générale, vous ne souhaitez pas remplacer le servlet par défaut, sinon vous devrez vous occuper de toutes ses tâches, ce qui n’est pas vraiment trivial (bibliothèque d’utilitaires JSF OmniFaces a un code source ouvert).exemple ). C'est donc aussi un mauvais modèle d'URL pour les servlets. La raison pour laquelle les pages JSP n'atteignent pas ce servlet est due au fait que le servlet JSP interne du servletcontainer sera appelé, qui est déjà mappé par défaut sur le modèle d'URL plus spécifique _*.jsp_.

_<url-pattern></url-pattern>_

Ensuite, il y a aussi le modèle d'URL de chaîne vide. Ceci sera appelé lorsque la racine de contexte est demandée. Ceci est différent de l'approche <welcome-file> selon laquelle il n'est pas appelé lorsqu'un sous-dossier est demandé. Il s’agit probablement du modèle d’URL que vous recherchez si vous voulez un " servlet de page d’accueil ". Je dois seulement admettre que je m'attendais intuitivement à ce que le modèle d'URL de chaîne vide et le modèle d'URL slash _/_ soient définis exactement dans l'autre sens, ce qui me permet de comprendre qu'un grand nombre de débutants ont été déroutés. Mais c'est ce que c'est.

Contrôleur frontal

Si vous avez réellement l'intention de disposer d'un servlet de contrôleur frontal, il est préférable de le mapper sur un modèle d'URL plus spécifique, tel que _*.html_, _*.do_, _/pages/*_, _/app/*_, etc. Vous pouvez masquer le modèle d'URL du contrôleur frontal et couvrir les ressources statiques sur un modèle d'URL commun tel que _/resources/*_, _/static/*_ , etc. à l'aide d'un filtre de servlet. Voir aussi Comment empêcher les ressources statiques d'être gérées par une servlet de contrôleur frontal mappée sur/* . Il convient de noter que Spring MVC dispose d'un servlet de ressources statiques intégré. Vous pouvez donc mapper son contrôleur frontal sur _/_ si vous configurez un modèle d'URL commun pour les ressources statiques dans Spring. Voir aussi Comment gérer le contenu statique dans Spring MVC?

254
BalusC

J'aimerais compléter la réponse de BalusC avec les règles de mappage et un exemple.

Règles de mappage de la spécification Servlet 2.5:

  1. URL exacte de la carte
  2. Carte des chemins génériques
  3. Extensions de carte
  4. Mapper sur le servlet par défaut

Dans notre exemple, il y a trois servlets./est le servlet par défaut installé par nous. Tomcat installe deux servlets pour servir jsp et jspx. Donc, pour mapper http://Host:port/context/hello

  1. Aucun servlet d'URL exact n'est installé, ensuite.
  2. Aucun servlet générique n'est installé sur les servlets, ensuite.
  3. Ne correspond à aucune extension, ensuite.
  4. Mapper sur la servlet par défaut, revenir.

Pour mapper http://Host:port/context/hello.jsp

  1. Aucun servlet d'URL exact n'est installé, ensuite.
  2. Aucun servlet générique n'est installé sur les servlets, ensuite.
  3. Servlet d'extension trouvée, retour.
42
Candy Chiu

Peut-être devriez-vous également savoir comment les URL sont mappées, car j'ai souffert de 404 pendant des heures. Il existe deux types de gestionnaires traitant les demandes. BeanNameUrlHandlerMapping et SimpleUrlHandlerMapping. Lorsque nous avons défini un servlet-mapping, nous utilisons SimpleUrlHandlerMapping. Une chose que nous devons savoir est que ces deux gestionnaires partagent une propriété commune appelée alwaysUseFullPath dont la valeur par défaut est false.

false signifie ici que Spring n'utilisera pas le chemin complet pour mapper une URL sur un contrôleur. Qu'est-ce que ça veut dire? Cela signifie que lorsque vous définissez un servlet-mapping:

<servlet-mapping>
    <servlet-name>viewServlet</servlet-name>
    <url-pattern>/perfix/*</url-pattern>
</servlet-mapping>

le gestionnaire utilisera réellement la partie * pour trouver le contrôleur. Par exemple, le contrôleur suivant sera confronté à une erreur 404 lorsque vous le demanderez à l'aide de /perfix/api/feature/doSomething

@Controller()
@RequestMapping("/perfix/api/feature")
public class MyController {
    @RequestMapping(value = "/doSomething", method = RequestMethod.GET) 
    @ResponseBody
    public String doSomething(HttpServletRequest request) {
        ....
    }
}

C'est un match parfait, non? Mais pourquoi 404. Comme mentionné précédemment, la valeur par défaut de alwaysUseFullPath est false, ce qui signifie que dans votre demande, seul /api/feature/doSomething est utilisé pour trouver un contrôleur correspondant, mais aucun contrôleur ne se soucie de ce chemin. Vous devez changer votre adresse URL en /perfix/perfix/api/feature/doSomething ou supprimer perfix de la base MyController @RequestingMapping.

22
hakunami

Je pense que la réponse de Candy est généralement correcte. Il y a une petite partie je pense autrement.

Pour mapper l'hôte: port/context/hello.jsp

  1. Aucun servlet d'URL exact n'est installé, ensuite.
  2. servlets de joker trouvés, return.

Je crois que la raison pour laquelle "/ *" ne correspond pas à l'hôte: port/contexte/bonjour car il traite "/ bonjour" comme un chemin plutôt qu'un fichier (car il n'a pas d'extension).

8
hehe

La différence essentielle entre /* et / est qu’un servlet avec mappage /* sera sélectionné avant tout servlet avec un mappage d’extension (comme *.html), tandis qu’un servlet avec mappage / sera sélectionné uniquement une fois les mappages d'extension envisagés (et sera utilisé pour toute demande ne correspondant à rien d'autre - il s'agit du "servlet par défaut").

En particulier, un mappage /* sera toujours sélectionné avant un mappage /. Avoir l'un ou l'autre empêche toute requête d'atteindre le servlet par défaut du conteneur.

L'un ou l'autre sera sélectionné uniquement après les mappages de servlet qui correspondent exactement (comme /foo/bar) et ceux qui sont des mappages de chemin plus longs que /* (comme /foo/*). Notez que le mappage de chaîne vide est une correspondance exacte pour la racine de contexte (http://Host:port/context/).

Voir le chapitre 12 de la spécification de servlet Java, disponible dans la version 3.1 à l'adresse http://download.Oracle.com/otndocs/jcp/servlet-3_1-fr-eval. -spec/index.html .

2