Grundlegende Konfigurationsdateien sind nicht intuitiv.
Wenn ich ein einfaches Hallo-Welt-Beispiel erstelle und dann home.jsp
in home.html
umbenenne und servlet-context.xml
-Datei aus bearbeite
<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>
zu
<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>
Ich bekomme einen Fehler
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'
Warum? Was bedeutet suffix
-Eigenschaft?
UPDATE
Mein Controller ist folgt. Wie Sie sehen, enthält es keine Dateierweiterung
@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";
}
}
Hintergrund des Problems
Das Erste, was Sie verstehen sollten, ist Folgendes: Es ist NICHT der Frühling, der die JSP-Dateien rendert. Es ist JspServlet (org.Apache.jasper.servlet.JspServlet), das dies tut. Dieses Servlet wird mit Tomcat (Jasper Compiler) geliefert, nicht mit Spring. Diesem JspServlet ist bekannt, wie man eine Jsp-Seite kompiliert und sie als HTML-Text an den Client zurückgibt. Das JspServlet in Tomcat verarbeitet standardmäßig nur Anforderungen, die zwei Mustern entsprechen: * .jsp und * .jspx.
Wenn der Frühling nun die Ansicht mit InternalResourceView
(oder JstlView
) rendert, finden tatsächlich drei Dinge statt:
"public ModelAndView doSomething() { return new ModelAndView("home") }"
)RequestDispatcher
weiß, dass jede * .jsp-Anfrage an JspServlet weitergeleitet werden sollte (da dies die Standardkonfiguration von Tomcat ist)Wenn Sie einfach den Namen der Ansicht in home.html ändern, kann Tomcat nicht die Anfrage bearbeiten. Dies liegt daran, dass keine Servlet-Verarbeitung für * .html-Anforderungen erfolgt.
Lösung
Wie löst man das? Es gibt drei naheliegendste Lösungen:
Erstkonfiguration (nur mit jsp)
Nehmen wir zunächst an, wir konfigurieren spring ohne xml-Dateien (nur basierend auf @ Configuration Annotation und der WebApplicationInitializer-Oberfläche von spring).
Die Grundkonfiguration würde folgen
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;
}
}
Im obigen Beispiel verwende ich UrlBasedViewResolver mit der Sicherungssichtklasse JstlView, aber Sie können InternalResourceViewResolver verwenden, da dies in Ihrem Beispiel keine Rolle spielt.
Das obige Beispiel konfiguriert eine Anwendung mit nur einem View Resolver, der JSP-Dateien verarbeitet, die mit .jsp
Enden. HINWEIS: Wie eingangs erwähnt, verwendet JstlView den RequestDispatcher von Tomcat, um die Anforderung an JspSevlet weiterzuleiten und den Jsp in HTML zu kompilieren.
Implementierung auf Lösung 1 - Machen Sie den HTML-Code als Ressourcendatei verfügbar:
Wir ändern die WebConfig-Klasse, um eine neue Ressourcenübereinstimmung hinzuzufügen. Außerdem müssen wir den jstlViewResolver so ändern, dass er weder Präfix noch Suffix annimmt:
@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;
// }
}
Indem wir dies hinzufügen, sagen wir, dass jede Anforderung, die an http: //my.server/someurl/resources/ geht, dem Ressourcenverzeichnis in Ihrem Webverzeichnis zugeordnet wird. Wenn Sie also Ihre home.html im Ressourcenverzeichnis ablegen und Ihren Browser auf http: //my.server/someurl/resources/home.html verweisen, wird die Datei geliefert. Um dies von Ihrem Controller zu handhaben, geben Sie den vollständigen Pfad zur Ressource zurück:
@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
}
}
Wenn Sie im selben Verzeichnis einige JSP-Dateien (nicht nur * .html-Dateien) ablegen, sagen Sie home_dynamic.jsp im selben Ressourcenverzeichnis, Sie können auf ähnliche Weise darauf zugreifen, müssen jedoch den tatsächlichen Pfad auf dem Server verwenden. Der Pfad beginnt nicht mit/someurl /, da dies die Zuordnung nur für HTML-Ressourcen ist, die mit .html enden. In diesem Zusammenhang ist jsp eine dynamische Ressource, auf die JspServlet letztendlich über den tatsächlichen Pfad auf der Festplatte zugreift. Der richtige Weg, um auf den JSP zuzugreifen, ist also:
@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
}
Um dies in einer XML-basierten Konfiguration zu erreichen, müssen Sie Folgendes verwenden:
<mvc:resources mapping="/someurl/resources/**" location="/resources/" />
und ändere deinen jstl view resolver:
<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>
Implementierung auf Lösung 2 :
In dieser Option verwenden wir das Tomcat-JspServlet, um auch statische Dateien zu verarbeiten. Infolgedessen können Sie in Ihren HTML-Dateien JSP-Tags verwenden :) Sie haben natürlich die Wahl, ob Sie dies tun oder nicht. Höchstwahrscheinlich möchten Sie nur HTML verwenden, verwenden Sie also einfach keine JSP-Tags, und der Inhalt wird so bereitgestellt, als wäre er statisches HTML.
Zuerst löschen wir Preffix und Suffix für View Resolver wie im vorherigen Beispiel:
@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;
}
}
Jetzt fügen wir JspServlet hinzu, um auch * .html-Dateien zu verarbeiten:
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");
}
}
Wichtig ist, dass Sie, um diese Klasse verfügbar zu machen, die Datei jasper.jar aus der Tomcat-Installation hinzufügen müssen, um die Kompilierungszeit zu nutzen. Wenn Sie eine maven-App haben, ist dies relativ einfach, indem Sie den scope = verwenden, der für das Glas bereitgestellt wird. Die Abhängigkeit in maven sieht folgendermaßen aus:
<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>
Wenn Sie es in XML-Weise tun möchten. Sie müssten ein jsp-Servlet registrieren, um HTML-Anforderungen zu verarbeiten. Fügen Sie daher den folgenden Eintrag zu Ihrer web.xml hinzu
<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>
Jetzt können Sie in Ihrem Controller wie im vorherigen Beispiel auf HTML- und JSP-Dateien zugreifen. Der Vorteil ist, dass in Lösung 1 keine zusätzliche Zuordnung "/ someurl /" erforderlich ist. Ihr Controller sieht folgendermaßen aus:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home.html");
}
Um auf Ihre JSP zu verweisen, machen Sie genau das Gleiche:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home_dynamic.jsp");
}
Implementierung auf Lösung 3 :
Die dritte Lösung ist eine Kombination aus Lösung 1 und Lösung 2. Hier möchten wir also alle Anforderungen an * .html an ein anderes Servlet übergeben. Sie können Ihr eigenes Servlet schreiben oder nach einem geeigneten Kandidaten für ein bereits vorhandenes Servlet suchen.
Wie oben bereinigen wir zuerst das Präfix und das Suffix für den View Resolver:
@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;
}
}
Anstatt das JspServlet von Tomcat zu verwenden, schreiben wir jetzt unser eigenes Servlet (oder verwenden ein vorhandenes wieder):
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();
}
}
}
}
}
Wir weisen den Frühling nun an, alle Anfragen an * .html an unser Servlet weiterzuleiten
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");
}
}
Der Vorteil (oder Nachteil, hängt davon ab, was Sie wollen) ist, dass JSP-Tags offensichtlich nicht verarbeitet werden. Ihr Controller sieht wie gewohnt aus:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home.html");
}
Und für jsp:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
// (...)
return new ModelAndView("/resources/home_dynamic.jsp");
}
Resolver-Klasse wird verwendet, um Ressourcen einer Ansichtsklasse aufzulösen, Ansichtsklasse wiederum generiert die Ansichten aus Ressourcen. Zum Beispiel mit einem typischen InternalResourceViewResolver wie folgt:
<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>
Ein Ansichtsname "home" wird als "/WEB-INT/views/home.jsp" zugeordnet und dann mithilfe der Ansichtsklasse InternalResourceView (für JSP) in eine JSP-Ansicht übersetzt. Wenn Sie den Suffixwert durch ".html" ersetzen, kann Spring die spezifische Ressource "/WEB-INT/views/home.html" abrufen, weiß jedoch nicht, wie er generiert werden soll.
Einfache .html-Dateien sind statisch und benötigen keinen speziellen ViewResolver. Sie sollten einen statischen Ordner für Ihre HTML-Seiten einrichten (siehe hier ).
Zum Beispiel:
<mvc:resources mapping="/static/**" location="/static/" />
nun, es scheint, dass Sie nicht die Reihenfolge der Ansicht festlegen.
wenn Ihr Projekt beispielsweise über eine Ansicht wie jsp, json, Velocity, Freemarker usw. verfügt, können Sie alle verwenden (möglicherweise benötigen Sie eine neue Version von Spring 3.1+), aber nur eine Ansicht wird für das Rendern ausgewählt an Client, das hängt von der Reihenfolge Ihrer Ansicht ab, je niedriger die Reihenfolge, desto lieber die Ansicht.
sie setzen zum Beispiel jsp views Reihenfolge auf 1, und die Reihenfolge der freemarker view ist 2, beide Ansichtsnamen sind "home", der Frühling wählt view.jsp (wenn Sie ein Suffix setzen) zu .jsp). Wenn Ihr Ansichtsname "index" lautet, keine index.jsp, aber index.ftl (wenn Sie die Ansicht von freemarker auf .ftl setzen), wählt spring die spätere.
den Beispielcode mit der Java-Konfiguration von Spring können Sie problemlos in den XML-Stil konvertieren.
@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;
}
bitte sehen Sie die setOrder () Methode!
json, Jsonp und andere Arten von Ansichten verwenden möglicherweise ontentNegotiation, und Sie finden es in den Dokumenten von Spring.
schließlich die html-Ansicht, ich meine total statische Dateien, die standardmäßig nicht unterstützt wird. Ich nehme an, dass die statische Datei nicht von Java gerendert werden muss. Sie können die statische Zuordnung mit dem folgenden Code verwenden:
<mvc:resources mapping="/static/**" location="/static/" />
oder benutze 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);
}
und in Ihrer @RequestMapping-Methode sollten Sie Weiterleitung it!
nun, wenn Sie keine Umleitung wünschen, setzen Sie einfach die HTML-Ansicht auf eine dynamische-Ansicht (Freemark, Velecity usw.), was in Ordnung sein wird!
hoffe es nützlich!
Spring MVC erlaubt es nicht, statische Ressourcen über Controller zu übertragen. Wie Arun sagte, sollte es durch resources
bedient werden.
Korrigieren Sie mich, wenn ich falsch liege, aber Sie wollen einen index.html
als Startseite. Um dies zu erreichen, sollte ein Controller (zB IndexController) /index.html
zugeordnet sein. Dann sollten Sie in Ihrem web.xml
konfigurieren, dass Ihre Begrüßungsdatei index.html
lautet. Auf diese Weise sucht Ihr Container immer dann, wenn Sie auf das Stammverzeichnis Ihrer Anwendung zeigen, nach "/index.html" und sucht nach dem Controller, der der URL /index.html
zugeordnet ist.
Ihr Controller sollte also etwa so aussehen:
@Controller
@RequestMapping("/index.html")
public class MyIndexController {
@RequestMapping(method=RequestMethod.GET)
protected String gotoIndex(Model model) throws Exception {
return "myLandingPage";
}
}
Und in deiner web.xml
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
Hoffe das hilft.
Ich denke, dass InternalResourceViewResolver Servlets und Jsp-Dateien unterstützt. Das Suffix gemäß Spring's API-Javadocs ist das, was beim Erstellen einer URL an die Anzeige von Namen angehängt wird. Es ist nicht die Dateierweiterung, auch wenn sie sehr irreführend ist. Ich habe auf UrlBasedViewResolver setSuffix () class überprüft.
Wenn sie es als viewSuffix bezeichnen, könnte es wahrscheinlich sinnvoller sein, denke ich.
Sie haben dieses Problem, weil möglicherweise kein Servlet für die Zuordnung von * .html ..__ registriert ist.
so endet der Aufruf mit dem "Standard-Servlet", das bei einem Servlet-Mapping von/registriert ist, das wahrscheinlich Ihr DispatcherServlet ist.
Das Dispatcher-Servlet hat jetzt keinen Controller gefunden, der die Anfrage für home.html und damit die Meldung bearbeitet, die Ihnen angezeigt wird .
Um dieses Problem zu lösen, können Sie die * .html-Erweiterung registrieren, um von JSPServlet behandelt zu werden. Dann sollte es sauber funktionieren.