8

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";
    }
}
MrPlow
  • 1,295
  • 3
  • 26
  • 45

3 Answers3

9

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

Kuntal-G
  • 2,970
  • 16
  • 16
  • Thanks. One question though. The media types mention xml - application/xml . That accounts for html files as well? – MrPlow Jul 19 '14 at 09:32
  • yes you can use this for html . i have updated my answer.Please check – Kuntal-G Jul 19 '14 at 09:44
  • I see. One more question. Where does the it look for the files? It doesn't seem to be able to find my views. I'll update the OP with current configuration – MrPlow Jul 19 '14 at 11:43
  • i have updated my answer.Please update your configuration.Hopefully it should resolve your issue! – Kuntal-G Jul 19 '14 at 13:13
  • I've done as you said but I'm still getting the same error. I've also tried changing `return "layout.html;"` to `return "html/layout.html;"` but no difference – MrPlow Jul 19 '14 at 14:21
  • just change your controller & check whether it is able to identify the static html files or not: @Controller public class LayoutController { @RequestMapping(value = "/staticPage", method = RequestMethod.GET) public String getIndexPage() { return "redirect:/html/final.htm"; } } – Kuntal-G Jul 19 '14 at 14:41
  • doesn't work. I've done what you said (except my page is layout.html not final.htm) and I still get `javax.servlet.ServletException: Could not resolve view with name 'redirect:/html/layout.html' in servlet with name 'springDispatcherServlet'` – MrPlow Jul 19 '14 at 14:45
  • can you just comment your resolver and check whether the html files are mapping properly or not..just commment the content negotiating resolver or any other resolver that you have in your config file and try with this only : @Controller public class LayoutController { @RequestMapping("/staticPage") public String getIndexPage() { return "layout.htm"; } } – Kuntal-G Jul 19 '14 at 14:49
  • I've commented out the view resolver and now It shows the page, however it doesn't load any other resources with the page (css, js, angular) – MrPlow Jul 19 '14 at 14:52
  • in your html page how are you mapping your css or js? just do the things that i am posting in new answer.. – Kuntal-G Jul 19 '14 at 14:53
  • when I used Velocity then I used the mapping defined with mvc:resources in my webapp-config.xml. – MrPlow Jul 19 '14 at 14:56
  • please check my new answer for mapping css,js in your html page – Kuntal-G Jul 19 '14 at 15:11
  • Indeed that does work. Why did you fragment your answers so much? Could you compile all your answers into one so that I can accept it? – MrPlow Jul 19 '14 at 16:39
  • Glad to know it working for you now! I have compiled my answer into one place for better readability and easier understanding now. – Kuntal-G Jul 20 '14 at 09:47
2

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

Dani
  • 4,001
  • 7
  • 36
  • 60
  • Thanks. One question though. The media types mention xml - application/xml . That accounts for html files as well? – MrPlow Jul 19 '14 at 09:33
  • I don´t know if I am understanding well your question. But content type to HTML files is "text/html" instead of "application/xml". Searching in google you can find the differents content types for all resources. For other side, learn about @RequestMapping and consumes/produces attributes, are for specify in the method level all that questions. – Dani Jul 19 '14 at 12:08
1

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>
Resd
  • 11
  • 1