Which spring view resolver plays nice with angularjs? - spring

I'm writing a webapp using angularjs and spring mvc as a REST service provider and as a partial view provider(I'm also using angular-ui-router so that I can have multiple nested partials). I currently don't have any use for template languages since I plan on doing everything in angular, however every single view resolver I've tried has some type of template language which clashes with angular and either crashes the application and/or fills my logs with errors.
First I tried using InternalResourceViewResolver but no luck as it seems that it only expects .jsp files and won't show anything else.
Then I tried using Thymeleaf. Thymeleaf follows the XML standard which forced me to rewrite most of my html to follow the xml requirements, and after I'd done that it died upon encountering a && inside an ng-show directive. So no luck with that either.
Then I tried Velocity. I've had most luck with velocity so far. It serves up html files nicely, doesn't stop upon encountering parse errors and allows me to serve up partial views the same way InternalResourceViewResolver does. However upon encountering angular variables prefixed by $ Velocity tries to parse them as VTL variables and fills my logs with messages like
velocity - Null reference [template 'clients/createOrEdit.html', line 1, column 65] : $invalid cannot be resolved.
Everything keeps working as it should but I'm not the one to just leave errors be, and I've found no way of disabling VTL.
That's my current experience with view resolvers.
I've also had an idea to treat .html files as static resources(which they kinda are before angular does it's magic) using mvc:resources but without any view resolver my application failed to start even if I set the main layout.html to be the welcome-file in web.xml
My question is. What should I use as a view resolver so that it plays nice with angularjs, and if I should even use view resolvers?
EDIT: I'm trying to use the ContentNegotiatingViewResolver and I get:
DEBUG ContentNegotiatingViewResolver - Requested media types are [text/html] based on Accept header types and producible media types [*/*])
DEBUG ContentNegotiatingViewResolver - No acceptable view found; returning null
DEBUG DispatcherServlet - Could not complete request
javax.servlet.ServletException: Could not resolve view with name 'layout.html' in servlet with name 'springDispatcherServlet'
webapp-config.xml (contextconfig in dispatcher servlet)
<mvc:annotation-driven />
<!-- Resources -->
<mvc:resources location="/libs/" mapping="/libs/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/js/" mapping="/js/**" />
<!-- Angular application data -->
<mvc:resources location="/WEB-INF/appjs/" mapping="/appjs/**" />
<!-- View locations -->
<mvc:resources location="/WEB-INF/html/" mapping="/**"/>
<!-- Controllers -->
<context:component-scan base-package="com.mrplow.controller" />
<!-- Views -->
<util:map id="contentMediaTypes">
<entry key="json" value="application/json" />
<entry key="html" value="text/html" />
</util:map>
<!-- <util:list id="defaultViews"> -->
<!-- <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> -->
<!-- </util:list> -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="1"
p:ignoreAcceptHeader="false"
p:defaultContentType="text/html"
p:mediaTypes-ref="contentMediaTypes" />
LayoutController.java
#Controller
#RequestMapping("/")
public class LayoutController {
#RequestMapping
public String getIndexPage() {
return "layout";
}
}

In order to use static resource(html,css,img,js) in spring, use a directory structure that looks like the following:
src/
package/
LayoutController.java
WebContent/
WEB-INF/
static/
html/
layout.html
images/
image.jpg
css/
test.css
js/
main.js
web.xml
springmvc-servlet.xml
#Controller
public class LayoutController {
#RequestMapping("/staticPage")
public String getIndexPage() {
return "layout.htm";
} }
<!-- in spring config file -->
<mvc:resources mapping="/static/**" location="/WEB-INF/static/" />
layout.html
<h1>Page with image</h1>
<img src="/static/img/image.jpg"/>
Note you have to mention /static/img/image.jpg not just /image.jpg ..Same applies for css and js.
OR
You can also use content negotiating view resolver as shown below:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="rss" value="application/rss+xml" />
<entry key="html" value="text/html"/>
</map>
</property>
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
</bean>
Spring MVC will use “ContentNegotiatingViewResolver” (order=1) to return a suitable view (based on file extension declared in “mediaTypes” property), if not match, then use “InternalResourceViewResolver” (order=2) to return a default JSP page.
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="2" />
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
Now from your jsp you can redirect to your static html page as well
#Controller
public class LayoutController {
#RequestMapping("/index")
public String getIndexPage() {
return "index";
}
}
index.jsp
<form:form method="GET" action="/static/html/layout.html">
Now try to access your service through http://yourapp.com//index show the form action mentioned above.It will show the layout.html
click on a button or submit in jsp page to invoke the layout.html page

I think ContentNegotiatingViewResolver is the best view resolver, because you will be able to integrate it with Jackson2 to response the data in JSON, XML or HTML text among others responses types that you need.
For example, look this approach.
http://hillert.blogspot.com.es/2011/01/rest-with-spring-contentnegotiatingview.html

For people who look for how to make the solution with ContentNegotiatingViewResolver to work in new version of Spring:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="defaultViews">
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</property>
</bean>
And after that configure extensions what you need in ContentNegotiationManagerFactoryBean:
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<props>
<prop key="json">application/json</prop>
<prop key="html">text/html</prop>
<prop key="xml">application/xml</prop>
// and so on
</props>
</property>
<property name="ignoreAcceptHeader" value="true"/>
</bean>

Related

How to load html views in spring?

My Current Working code for resolving html views is like this
<mvc:resources mapping="/static/**" location="/WEB-INF/static/html/" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="" />
<property name="suffix" value=".html" />
</bean>
But i need to return view such as
return "/static/html/index";
How can i make it like this?
return "index";
if you are using spring Boot, it will automatically add static web resources located within any of the following directories:
/META-INF/resources/
/resources/
/static/
/public/
in case of consuming , restful web service it might be a good approach if you put the resources in to the public folder,and this is how your controller should look like.
#Controller
class Controller{
#RequestMapping("/")
public String index() {
return "index.html";
}
}
Change the prefix to /static/html/
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/static/html/" />
<property name="suffix" value=".html" />
</bean>
So when you will return as "index" it will change to /static/html/index.html.

How to redirect welcome page using abstract controller class in spring MVC?

I try to do multipage form handling through abstract wizzard form controller. But in URL shows http://localhost:8080/SpringMVC/ like this. Could not map the WelcomePage.jsp file.
My dispatcher servlet is this
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean name="/WelcomePage" class="com.mkyong.common.controller.WelcomeController" />
<bean class="com.mkyong.common.controller.UserController" >
<property name="pages">
<list>
<!-- follow sequence -->
<value>Page1Form</value> <!-- page1 -->
<value>Page2Form</value> <!-- page2 -->
<value>Page3Form</value> <!-- page3 -->
</list>
</property>
<property name="validator">
<bean class="com.mkyong.common.validator.UserValidator" />
</property>
</bean>
<!-- Register User.properties for validation error message -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="User" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
And my controller code is here mentioned . If I run my project shows warning
No mapping found for HTTP request with URI [/SpringMVC/] in DispatcherServlet with name 'mvc-dispatcher'
public class WelcomeController extends AbstractController{
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("WelcomePage");
}
}
In Spring MVC, ControllerClassNameHandlerMapping use convention to map requested URL to Controller (convention over configuration). It takes the Class name, remove the ‘Controller’ suffix if exists and return the remaining text, lower-cased and with a leading “/”.
in your case, you do not have any controller mapped with "/" url, so for accedding to your welcome page you must use : http://localhost:8080/SpringMVC/welcome

ControllerAdvice with ExceptionHandler and ModelAndView not loading the correct view on screen

I need help on this, please take a look at my code:
#ControllerAdvice
#EnableWebMvc
public class GlobalExceptionController {
#ExceptionHandler(CustomGenericException.class)
public ModelAndView handleCustomException(CustomGenericException ex) {
// create the model and view with the tiles View pointing to error jsp page
ModelAndView model = new ModelAndView("pagina.erro");
model.addObject("errCode", ex.getErrCode());
model.addObject("errMsg", ex.getErrMsg());
return model;
}
...
Tiles configuration:
<definition name="pagina.erro" extends="baseLayout">
<put-attribute name="titlepagina" value="Página de Erro" />
<put-attribute name="body" value="/WEB-INF/jsp/error/erro-generico.jsp" />
</definition>
In my Spring configuration xml I have:
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
The page erro-generico.jsp is being loaded because if I put a wrong code in it errors will be shown on eclipse console.
The problem is: the page is not shwowing, the actual page keeps showing on the browser, not even the URL changes.
What could be wrong?
The problem is that the view resolver being used is not compatible with Tiles, it only works for pure JSPs.
It's possible to configure a view resolver for Tiles by following these instructions - 14.3.2. How to integrate Tiles:
bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/widgets.xml</value>
<value>/WEB-INF/defs/administrator.xml</value>
<value>/WEB-INF/defs/customer.xml</value>
<value>/WEB-INF/defs/templates.xml</value>
</list>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>

Accessing data from session-scoped beans in JSP files

I'm trying to get started with session-scoped beans in Spring Web MVC 3. I put this line in my dispatcher configuration:
<bean id="userInfo" class="net.sandbox.sessionbeans.UserInfo" scope="session" />
Here is net.sandbox.sessionbeans.UserInfo:
package net.sandbox.sessionbeans;
public class UserInfo {
public String username;
public UserInfo() {
this.username = "Unregistered User";
}
}
How can I access session-scoped beans inside the JSP files that represent the View part of my application? I tried this...
<p align="right">${userInfo.username}</p>
... but that didn't give me the expected result, i.e.
<p align="right">Unregistered User</p>
Instead I just get
<p align="right"></p>
What am I doing wrong?
You can do it as you show in your question. The problem is probably in your configuration. Look if you expose your beans in the view, like this:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
<property name="exposeContextBeansAsAttributes" value="true" />
</bean>
You can expose individual beans to JSTL with Spring.
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="exposedContextBeanNames">
<list>
<value>someBean</value>
<value>someOtherBean</value>
</list>
</property>
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
This answer is partially based on some advice that was posted in the question's comments but was later deleted by the poster. I added this to every JSP page that needs to use the bean:
<jsp:useBean id="userInfo" scope="session" class="net.sandbox.sessionbeans.UserInfo" />
I then found this article detailing how you can use beans in a JSP page.
You need to make sure that you have
<aop:scoped-proxy/>
enabled in your xml configuration.
For Example:
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
You can read more about it in Spring reference guide, "Bean scopes" chapter.
Elaborating on #sinuhepop suggestion below.
An easy way to do this is to configure spring so that it exposes the spring bean ids as varibales to JSTL this can be done by setting the exposeContextBeansAsAttributes property to true
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="exposeContextBeansAsAttributes" value="true"/>
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
If you have a bean configured like so
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value=WebApplicationContext.SCOPE_SESSION)
public class SomeBean {
}
Then in the JSP page you can access the bean using the name ${someBean.someProp} because that is the name that Spring will auto assign to the SomeBean unless the bean is defined with a name such as #Component("someName") then the JSTL would be ${someName.someProp}
An update to this answer for those, who want to use Spring 5 Java configuration. Addding this to your WebMvcConfigurer
#Override
public void configureViewResolvers(ViewResolverRegistry registry){
InternalResourceViewResolver resolver = new InternalResourceViewResolver("/WEB-INF/view", ".jsp");
resolver.setExposeContextBeansAsAttributes(true);
registry.viewResolver(resolver);
}
is equivalent to this XML config mentioned by others:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
<property name="exposeContextBeansAsAttributes" value="true" />
</bean>
Note, that the "convenient" fluent API of the registry (registry.jsp(). ...) does not offer you the possibility to configure the exposeContextBean.... properties.
If possible, you should consider using exposeContextBeanNames. Use constants as much as possible for your bean names, though, to avoid duplicating string literals in your code. So maybe define a String Array within some class, which collects all theses constants and exposes them to your view resolver.

How to use multiple ViewResolvers in Spring?

I am working on a web app where I have most of my pages making use of apache tiles (2.1.2), but a few of them need to just be plain jsps.
I am having a problem in that both an InternalResourceViewResolver and a UrlBasedViewResolver will try to resolve the view no matter what, so that no matter which ordering I use, it will either fail on the plain JSP pages, or on the tiles pages.
Here is the config:
<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<property name="order" value="0"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
<property name="order" value="1"/>
</bean>
To make it more clear what I am trying to do, I need to be able to have view states like this:
<view-state id="someState" view="/someDir/foo"><!--render foo.jsp -->
<transition on="foo" to="bar"/>
</view-state>
<view-state id="someState" view="something.core"><!--render tile defintion named 'something.core' -->
<transition on="foo" to="bar"/>
</view-state>
Does anyone know how to configure things so that I can get it to render tiles definitions and plain jsps?
As you say, you cannot chain these together. The javadoc for both states clearly that they must both be at the end of the resolver chain.
I suggest that if you really need to use these togather, then you write a simple custom implementation of ViewResolver which takes the view name, and decides which of your two "real" view resolvers to delegate to. This assumes that you can tell which resolver to call based on the view name.
So you'd define a custom ViewResolver like this:
public class MyViewResolver implements ViewResolver {
private ViewResolver tilesResolver;
private ViewResolver jspResolver;
public void setJspResolver(ViewResolver jspResolver) {
this.jspResolver = jspResolver;
}
public void setTilesResolver(ViewResolver tilesResolver) {
this.tilesResolver = tilesResolver;
}
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (isTilesView(viewName)) {
return tilesResolver.resolveViewName(viewName, locale);
} else {
return jspResolver.resolveViewName(viewName, locale);
}
}
private boolean isTilesView(String viewName) {
.....
}
}
You'd need to implement the isTilesView method to decide which resolver to delegate to.
In the XML config, define this new view resolver, and make sure it appears before the other ones.
<bean class="MyViewResolver">
<property name="tilesResolver" ref="tilesViewResolver"/>
<property name="jspResolver" ref="viewResolver"/>
</bean>
I've just solved the same problem by splitting the *-servlet.xml config file in two; in my case the main application uses Tiles, but I want QUnit tests to be simple JSPs.
app-servlet.xml contains only the Tiles view resolver, tests-servlet.xml only contains the JSP view resolver and web.xml mappings are dispatching requests to the correct servlet basing on the URL.
<servlet-mapping>
<servlet-name>app</servlet-name> <!-- will reach app-servlet.xml -->
<url-pattern>/foo</url-pattern> <!-- will use "foo" Tile -->
<url-pattern>/bar</url-pattern> <!-- will use "bar" Tile -->
</servlet-mapping>
<servlet-mapping>
<servlet-name>tests</servlet-name> <!-- will reach tests-servlet.xml -->
<url-pattern>/foo-test</url-pattern> <!-- will use foo-test.jsp -->
<url-pattern>/bar-test</url-pattern> <!-- will use bar-test.jsp -->
</servlet-mapping>
It looks like you're on the right track, but the thing to bear in mind is that some view resolvers behave as if they have always resolved the view. You need to make sure to put such resolvers last in your ordering. I believe the Tiles view is one such.
Edit: whoops... yes, the other poster is correct, both of these resolvers will do 'always match' so you can't use them both in a chain. Another alterative would be to try to extend the TilesView to do a simple JSP render if it cant find a configured tile view.
Yes you can use any number of view resolver in your your project.
So you can use both 'tiles View resolver' and 'Internal view resolver' in same project. .
you have to configure a ContentNegotiatingViewResolver . .
and give order value in your view resolvers.
<property name="order" value="int Value here" />
like I have given tiles view resolver 2 and internalviewresolver 3. .It will first check in tiles definitions if a view is not found in tiles it will be checked in InternaiViewResolver
here is some configurations that works for me.
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="html" value="text/html" />
</map>
</property>
<property name="parameterName" value="accept"></property>
<property name="favorParameter" value="true"></property>
<property name="defaultContentType" value="text/html"></property>
<property name="viewResolvers">
<list>
<ref bean="tilesViewResolver" />
<ref bean="internalViewResolver" />
</list>
</property>
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
<!-- Configures the Tiles layout system -->
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"
id="tilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/layouts/layouts.xml</value>
<!-- Scan views directory for Tiles configurations -->
<value>/WEB-INF/views/**/views.xml</value>
</list>
</property>
</bean>
<bean id="tilesViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver"
p:viewClass="org.springframework.web.servlet.view.tiles2.TilesView">
<property name="order" value="3" />
</bean>
<bean id="internalViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="2" />
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
I resolved this issue by simply adding tiles definition for plain jsp's layout, like this:
<definition name="plain-jsp.layout" template="/WEB-INF/layouts/plainJsp.jspx" >
<put-attribute name="content" value=""/>
</definition>
Then you just can use this layout as template for including your simple jsp files.
<definition name="catalog/details" extends="plain-jsp.layout">
<put-attribute name="content" value="/WEB-INF/views/catalog/details.jspx"/>
</definition>
And layout template file:
<html xmlns:tiles="http://tiles.apache.org/tags-tiles"
xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
<jsp:output doctype-root-element="HTML"/>
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:directive.page pageEncoding="UTF-8" />
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=8" />
</head>
<body>
<div id="content">
<tiles:insertAttribute name="content"/>
</div>
</body>
</html>

Resources