Comment établir la correspondance entre les demandes et le fichier HTML dans MVC du printemps?

Les fichiers de configuration de base de

semblent peu intuitifs.

si je crée simple hello world exemple, puis renommer home.jsp en home.html et éditer servlet-context.xml fichier de

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean> 

à

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".html" />
</beans:bean>

je commence à obtenir une erreur

WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/myapp/WEB-INF/views/home.html] in DispatcherServlet with name 'appServlet'

pourquoi? Que signifie suffix propriété?

mise à JOUR

mon contrôleur suit. Comme vous le voyez, il ne contient pas d'extension de fichier

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    /**
     * Simply selects the home view to render by returning its name.
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );

        return "home";
    }

}
24
demandé sur Suzan Cioc 2013-05-17 02:32:25

7 réponses

Fond du problème

la première chose à comprendre est la suivante: ce n'est pas le ressort qui rend les fichiers jsp. Il s'agit de JspServlet (org.Apache.Jasper.servlet.JspServlet) qui le fait. Ce servlet est livré avec Tomcat (jasper compiler) pas avec spring. Ce JspServlet sait comment compiler la page jsp et comment la retourner sous forme de texte html au client. Le JspServlet de tomcat ne traite par défaut que les requêtes correspondant à deux modèle.* :jsp et *.jspx.

maintenant que le printemps rend la vue avec InternalResourceView (ou JstlView ), trois choses ont vraiment lieu:

  1. récupérez tous les paramètres du modèle (retournés par votre méthode de gestionnaire de contrôleur i.e. "public ModelAndView doSomething() { return new ModelAndView("home") }" )
  2. exposer ces paramètres de modèle comme attributs de requête (de sorte qu'il peut être lu par JspServlet)
  3. envoyer la demande à JspServlet. RequestDispatcher sait que chacun *.la requête jsp doit être transmise à JspServlet (parce que c'est la configuration par défaut de tomcat)

quand vous changez simplement le nom de la vue à la maison.html tomcat va et non savoir comment traiter la demande. C'est parce qu'il n'y a pas de servlet de manutention *.html demandes.

Solution

comment résoudre ceci. Il y a trois la plupart solutions évidentes:

  1. exposer le html comme un fichier de ressource
  2. donner instruction au JspServlet de manipuler aussi *.html les demandes
  3. écrivez votre propre servlet (ou passez à une autre demande de servlet existante à *.HTML.)

configuration initiale (ne manipulant que jsp)

supposons D'abord que nous configurons spring sans fichiers xml (seulement en se basant sur @Configuration d'annotation et de printemps WebApplicationInitializer interface).

la configuration de base serait la suivante

public class MyWebApplicationContext extends AnnotationConfigWebApplicationContext {
  private static final String CONFIG_FILES_LOCATION = "my.application.root.config";

  public MyWebApplicationContext() {
    super();
    setConfigLocation(CONFIG_FILES_LOCATION);
  }

}

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addSpringDispatcherServlet(servletContext, context);

  }

  private void addSpringDispatcherServlet(ServletContext servletContext, WebApplicationContext context) {
    ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
      new DispatcherServlet(context));
    dispatcher.setLoadOnStartup(2);
    dispatcher.addMapping("/");
    dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");
  }
}

package my.application.root.config
// (...)

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    UrlBasedViewResolver resolver = new UrlBasedViewResolver();
    resolver.setPrefix("/WEB-INF/internal/");
    resolver.setViewClass(JstlView.class);
    resolver.setSuffix(".jsp");
    return resolver;
  }

}

dans l'exemple ci-dessus, J'utilise UrlBasedViewResolver avec la classe de rétro-affichage jstlview, mais vous pouvez utiliser InternalResourceViewResolver car dans votre exemple cela n'a pas d'importance.

ci-dessus exemple configure application avec un seul solveur de vue qui gère les fichiers jsp se terminant par .jsp . NOTE: Comme indiqué au début de JstlView utilise vraiment le RequestDispatcher de tomcat pour transmettre la requête à JspSevlet pour compiler le jsp en html.

Implementation on solution 1 - exposez le html comme un fichier ressource:

nous modifions la classe WebConfig pour ajouter de nouvelles ressources correspondant. Il faut aussi modifier le jstlViewResolver pour qu'il ne prenne ni préfixe ni suffixe:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/someurl/resources/**").addResourceLocations("/resources/");

  }

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    UrlBasedViewResolver resolver = new UrlBasedViewResolver();
    resolver.setPrefix(""); // NOTE: no preffix here
    resolver.setViewClass(JstlView.class);
    resolver.setSuffix(""); // NOTE: no suffix here
    return resolver;
  }

// NOTE: you can use InternalResourceViewResolver it does not matter 
//  @Bean(name = "internalResolver")
//  public ViewResolver internalViewResolver() {
//    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//    resolver.setPrefix("");
//    resolver.setSuffix("");
//    return resolver;
//  }
}

en ajoutant ceci nous disons que tout ce que chaque demande allant à http://my.server/someurl/ resources / est situé dans le répertoire des ressources de votre répertoire web. Donc, si vous mettez votre maison.html dans le répertoire des ressources et pointez votre navigateur vers http://my.serveur/someurl/ressources/home.html le fichier sera servi. Pour gérer cela par votre controller vous retournez alors le chemin complet à la ressource:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/someurl/resources/home.html"); // NOTE here there is /someurl/resources
    }

}

si vous placez dans le même répertoire des fichiers jsp (pas seulement *.html files), disons home_dynamic.jsp dans le même répertoire de ressources, vous pouvez y accéder même façon, mais vous devez utiliser le chemin d'accès sur le serveur. Le chemin ne pas commence avec /someurl/ parce que c'est le mappage seulement pour les ressources html se terminant avec .HTML.) Dans ce contexte, jsp est une ressource dynamique qui est finalement accessible par JspServlet en utilisant le chemin réel sur le disque. Donc, façon correcte d'accéder le jsp est:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp"); // NOTE here there is /resources (there is no /someurl/ because "someurl" is only for static resources 

}

pour réaliser ceci dans la configuration basée sur xml vous devez utiliser:

<mvc:resources mapping="/someurl/resources/**" location="/resources/" />

et modifier votre résolveur de vue jstl:

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Please NOTE that it does not matter if you use InternalResourceViewResolver or UrlBasedViewResolver as in annotations example -->
    <beans:property name="prefix" value="" />
    <beans:property name="suffix" value="" />
</beans:bean>

sur la mise en Œuvre de la solution 2 :

dans cette option, nous utilisons le JspServlet de tomcat pour gérer également les fichiers statiques. En conséquence, vous pouvez utiliser les tags jsp dans vos fichiers html:) c'est bien sûr votre choix si vous le faites ou pas. Très probablement, vous voulez utiliser HTML simple alors tout simplement ne pas utiliser les étiquettes jsp et le contenu sera servi comme s'il était HTML statique.

tout d'abord, nous supprimons preffix et suffix pour View resolver comme dans l'exemple précédent:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
    resolver.setPrefix("");
    resolver.setSuffix("");
    return resolver;
  }

}

maintenant nous ajoutons aussi JspServlet pour la manipulation *.fichiers html:

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addStaticHtmlFilesHandlingServlet(servletContext);
    addSpringDispatcherServlet(servletContext, context);

  }

 // (...)

  private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
    ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new JspServlet()); // org.apache.jasper.servlet.JspServlet
    servlet.setLoadOnStartup(1);
    servlet.addMapping("*.html");
  }

}

Important est que pour rendre cette classe disponible, vous devez ajouter le jasper.jar de l'installation de tomcat pour le temps de compilation. Si vous avez maven app, c'est vraiment facile en utilisant le scope=fourni pour le jar. La dépendance dans maven ressemblera à:

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jasper</artifactId>
    <version>${tomcat.libs.version}</version>
    <scope>provided</scope> <!--- NOTE: scope provided! -->
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jsp-api</artifactId>
    <version>${tomcat.libs.version}</version>
    <scope>provided</scope>
</dependency>

Si vous voulez le faire en xml. Vous devez enregistrer jsp servlet *.html demandes, vous devez ajouter l'entrée suivante à votre site web.xml

<servlet>
    <servlet-name>htmlServlet</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>htmlServlet</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>

maintenant dans votre contrôleur vous pouvez accéder aux fichiers html et jsp comme dans l'exemple précédent. L'avantage est qu'il n'y a pas de mappage supplémentaire"/ someurl / " qui était nécessaire dans la Solution 1. Votre contrôleur ressemblera à:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home.html"); 

}

pour pointer votre jsp vous faites exactement la même chose:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp");

}

sur la mise en Œuvre de la solution 3 :

la troisième solution est en quelque sorte une combinaison de la solution 1 et de la solution 2. Donc ici nous voulons transmettre toutes les demandes à *.html à un autre servlet. Vous pouvez écrire votre propre ou de chercher un bon candidat de servlet.

comme ci-dessus, nous nettoyons d'abord le préfixe et le suffixe pour le résolveur de vue:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
    resolver.setPrefix("");
    resolver.setSuffix("");
    return resolver;
  }

}

maintenant, au lieu d'utiliser le JspServlet de tomcat, nous écrivons notre propre servlet (ou réutilisons certains existants):

public class StaticFilesServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");

    String resourcePath = request.getRequestURI();
    if (resourcePath != null) {
      FileReader reader = null;
      try {
        URL fileResourceUrl = request.getServletContext().getResource(resourcePath);
        String filePath = fileResourceUrl.getPath();

        if (!new File(filePath).exists()) {
          throw new IllegalArgumentException("Resource can not be found: " + filePath);
        }
        reader = new FileReader(filePath);

        int c = 0;
        while (c != -1) {
          c = reader.read();
          if (c != -1) {
            response.getWriter().write(c);
          }
        }

      } finally {
        if (reader != null) {
          reader.close();
        }
      }
    }
  }
}

nous ordonnons maintenant au ressort de transmettre toutes les requêtes à *.html à notre servlet

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addStaticHtmlFilesHandlingServlet(servletContext);
    addSpringDispatcherServlet(servletContext, context);

  }

 // (...)

  private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
    ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new StaticFilesServlet());
    servlet.setLoadOnStartup(1);
    servlet.addMapping("*.html");

  }

}

L'avantage (ou l'inconvénient, dépend de ce que vous voulez) est que les étiquettes jsp ne seront évidemment pas traitées. Votre contrôleur regarde comme d'habitude:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home.html");

}

et pour jsp:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp");

}
29
répondu walkeros 2015-09-17 10:53:51
La classe

Resolver est utilisée pour résoudre les ressources d'une classe view, view class à son tour, génère les vues à partir des ressources. Par exemple, avec un résolveur de ressource interne typique comme ci-dessous:

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
</beans:bean>

Un nom de vue "à la maison" sera mappé en tant que "/WEB-INT/views/home.jsp" et ensuite traduit dans une vue JSP en utilisant view class InternalResourceView (qui est pour JSP). Si vous remplacez la valeur du suffixe ".html", le Printemps peut obtenir la ressource spécifique "/Web-INT/views / home.html", mais ne sait pas comment le générer.

5
répondu Megan Nguyen 2013-06-13 04:40:26

de la Plaine .les fichiers html sont statiques et n'ont pas besoin d'un ViewResolver spécial. Vous devez configurer un dossier statique pour vos pages html comme indiqué ici .

par exemple:

<mvc:resources mapping="/static/**" location="/static/" />
4
répondu Jonas 2017-05-23 12:02:41

Eh bien, il semble que vous n'avez pas mis l'ordre de la vue .

par exemple, si votre projet est vue comme jsp, json, de la vitesse, freemarker, etc. vous pouvez les utiliser tous(peut-être que vous avez besoin de la nouvelle version de spring, 3.1+), mais une seule vue sera sélectionnée pour rendre au client, cela dépend de l'ordre de votre vue, la plus basse de l'ordre, la préférence la vue .

par exemple, vous définissez jsp view' s de la commande est de 1, et freemarker vue' commande est de 2, à la fois de leur point de vue, le nom est "accueil", le printemps sera de choisir la vue.jsp(si vous avez réglé le suffixe .JSP.) Bien, si votre nom de vue est "index", pas d'index.jsp mais d'index.ftl(supposons que vous définissez freemarker' s de vue .ftl), le printemps choisira la plus tardive.

le code de l'échantillon en utilisant la config java de spring, vous pouvez facilement convertir en xml-style.

@Bean
public InternalResourceViewResolver jspViewResolver() {
    InternalResourceViewResolver jsp = new InternalResourceViewResolver();
    jsp.setOrder(4);
    jsp.setCache(true);
    jsp.setViewClass(org.springframework.web.servlet.view.JstlView.class);
    jsp.setPrefix("/WEB-INF/jsp/");
    jsp.setSuffix(".jsp");
    return jsp;
}

@Bean
public FreeMarkerViewResolver freeMarkerViewResolver() {
    FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
    viewResolver.setCache(true);
    viewResolver.setPrefix("");
    viewResolver.setSuffix(".ftl");
    viewResolver.setContentType(ViewConstants.MEDIA_TYPE_HTML);
    viewResolver.setRequestContextAttribute("request");
    viewResolver.setExposeSpringMacroHelpers(true);
    viewResolver.setExposeRequestAttributes(true);
    viewResolver.setExposeSessionAttributes(true);
    viewResolver.setOrder(2);
    return viewResolver;
}

consultez setOrder () méthode!

le json, jsonp et d'autres types de vues peuvent utiliser ontentNegotiation, et vous pouvez le trouver sur spring' s docs.

enfin, le vue html , je veux dire, fichiers totalement statiques , qui n'est pas supporté par défaut de ressort. Je suppose que le fichier statique desn' t besoin de rendre par java. vous pouvez utiliser la cartographie statique en utilisant le code ci-dessous:

<mvc:resources mapping="/static/**" location="/static/" />

ou de l'utilisation java config:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    int cachePeriod = 3600 * 24 * 15;
    registry.addResourceHandler("/static/**").addResourceLocations("/static/").setCachePeriod(cachePeriod);
    registry.addResourceHandler("/favicon.ico").addResourceLocations("/").setCachePeriod(cachePeriod);
    registry.addResourceHandler("/robots.txt").addResourceLocations("/").setCachePeriod(cachePeriod);
}

et dans votre méthode @RequestMapping, vous devriez rediriger it!

eh bien, si vous ne' t veulent redirection , il suffit de mettre le code html de vue d'un dynamique (freemark, velecity, etc), qui sera ok!

l'espoir qu'il soit utile!

2
répondu longkai 2014-02-08 15:16:46

ressort MVC ne vous permet pas de rendre les ressources statiques sur les contrôleurs. Comme L'a dit Arun, il devrait être servi par resources .

corrigez-moi si je me trompe, mais il semble que vous vouliez un index.html comme une première page. Pour ce faire, vous devez avoir un Controller (par exemple IndexController) mappé à /index.html . Ensuite, vous devez configurer dans votre web.xml votre yo dire que votre fichier de bienvenue est index.html . De cette façon, chaque fois que vous pointez vers la racine de votre application, votre conteneur va chercher un " / index.html" et à son tour, va chercher le contrôleur mappé à L'URL /index.html .

ainsi, votre contrôleur devrait ressembler à quelque chose comme ceci:

@Controller
@RequestMapping("/index.html")
public class MyIndexController {
    @RequestMapping(method=RequestMethod.GET)
    protected String gotoIndex(Model model) throws Exception {      
        return "myLandingPage";
    }
}

et dans votre web.xml

<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>

Espérons que cette aide.

1
répondu gnc 2014-01-17 04:20:23

je pense que le InternalResourceViewResolver supporte les servlets et les fichiers jsp. Le suffixe javadocs de L'API de Spring est celui qui "est ajouté pour afficher les noms lors de la construction d'une URL". Il n'est pas l'extension du fichier, même si c'est très trompeur. J'ai vérifié dans la classe UrlBasedViewResolver setSuffix() .

S'ils l'appellent viewSuffix, ça pourrait avoir plus de sens, je suppose.

0
répondu Indu Devanath 2013-05-17 03:18:07

vous avez ce problème car il se peut qu'il n'y ait pas de servlet enregistré pour mapping *.HTML.

donc l'appel se termine avec le" servlet par défaut", qui est enregistré avec un servlet-mapping de / qui est probablement votre DispatcherServlet.

Maintenant, le servlet de répartiteur ne trouve pas de contrôleur pour traiter les requêtes pour la maison.html et donc le message que vous voyez.

Pour résoudre ce problème, vous pouvez vous inscrire *.l'extension html doit être gérée par JSPServlet et alors elle devrait fonctionner proprement.

0
répondu Renganathan V 2014-02-18 05:35:25