English 中文(简体)
Spring MVC: Resolving the view based on User-Agent
原标题:

Spring Version: 2.5.6

I want to resolve the view to a specific velocity file based on the value of the User-Agent header.

My current line of thinking is an implementation similar to the UrlBasedViewResolver such that the user-agent value is Map d (via context) to a specific directory(value) based on a matching a regular expression(key).

I am almost certain there is an easier way.

A similar question was previously posted regarding Theme determination based on User-Agent. However, my understanding is that Themes relate more to static (css,js) content, not which file handles the actual response construction (HTML,XML,etc).

最佳回答

I am going with a custom view resolver as suggested in comments. (and upgrading my app to Spring 3.0.0)

问题回答

There is an other option suggested here

However I resolved Extending a ContentNegotiatingViewResolver and overriding the resolveViewName method, I called my ViewResolver HttpHeaderParamViewResolver. The extended method looks something like this:

@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
    //Get the HTTP Header param "User-Agent"
    String headerParamValue = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getHeader(headerParam);

    viewName = setViewName(viewName, headerParamValue);

    return super.resolveViewName(viewName, locale);
}

Where headerParam="User-Agent" (or any other HTTp Header parameter you like, this is defined in the bean xml), after that you evaluate it and determine the viewName. In My case the HttpHeaderParamViewResolver can be configured with a Map where the key is a prefix to be appended to the actual viewName and the value is a RegExp that will be used to evaluate the value of the header param. It looks something like this in the App Context XML:

<bean id="HttpHeaderViewResolver" class="com.application.viewresolver.HttpHeaderParamViewResolver">
    <property name="viewResolvers">
        <list>
            <ref bean="tilesViewResolver"/>
        </list>
    </property>
    <property name="headerParam" value="User-Agent"/>
    <property name="viewPrefixPattern">
        <map>
            <entry>
                <key>
                    <value>mobile-webkit</value>
                </key>
                <value>iPhone.*Apple.*Mobile.*Safari</value>
            </entry>
            <entry>
                <key>
                    <value>mobile-bb</value>
                </key>
                <value>BlackBerry([0-9]{0,4})([a-zA-Z])?</value>
            </entry>
        </map>
    </property>
</bean>

That way the if my controller calls a view called userDetails and is accessing the application with an IPhone the first pattern catchs it and appends the mobile-webkit suffix so the view is now mobile-webkit-userDetails and its then passed to the tilesViewResolver which generates the actual view.

I explored a lot of possibilities and I think this is the easiest and most flexible I was able to came up with. In this case the ability to choose an entire different view was critical because we support a wide variety of user agents, from WAP to IPhone 4 and WebKit capable mobiles, so views change dramatically from user agent to user agent. Other advantage is that you no longer require to handle this issue on the view since you can have views as specialized as you like. Other bright side is that you can implement this quite easily without having to remove or change the view resolvers you might already have since ContentNegotiatingViewResolver has the ability to delegate the view call to other view resolvers in the specific sequence you define.

The down side is that you may be tempted to over specialize the views and end up with a ton of view files rendering the app a maintainable nightmare.

Hope it is helpful.

I had the same problem a few months back!

On our mobile project (using Spring 2.5.6) we ended up using an interceptor with our SimpleUrlHandler. This caught all incoming requests and add -m.jsp to the end of any mobile requests.

It involved two steps:

1) declaring an interceptor to our standard URL Mapper:

 <bean id="handlerMapping"
 class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
 <!--   This interceptor catches all
 requests and redirects them to portal
 or mobile html content.
 --> 
<property name="interceptors">    <list>
      <ref bean="MultiViewController"/>    </list> </property>

and 2) implementing the Interceptor, which looked for the word Mobile in the user-agent.

public class MultiViewController extends HandlerInterceptorAdapter {

I talk about it in more detail on my blog (about the new exciting world of mobile web development) post: http://plumnash.com/it/iphone-web-development-using-spring/

An alternative that doesn t require configuration in a ViewResolver might involve a top-level Velocity file, then conditionally parsing sub-files that has something like the following.

#if ($userAgent1)
  #parse ("user-agent-1.vm")
#elseif ($userAgent2)
  #parse ("user-agent-2.vm")
#end

However, implementing a new or extending an existing ViewResolver is a pretty simple solution and would be the way I d go with.





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签