Spring MVC: разрешение представления на основе User-Agent

Spring Версия: 2.5.6

Я хочу разрешить представление для определенного файла скорости на основе значения заголовка User-Agent.

Мое текущее направление мышления - это реализация, аналогичная UrlBasedViewResolver, так что значение user-agent является Map'd (через контекст) для определенного каталога (значения) на основе соответствия регулярному выражению (ключу).

Я почти уверен, что есть более простой способ.

Ранее был опубликован аналогичный вопрос относительно определения темы на основе User-Agent. Однако я понимаю, что Темы больше связаны с статическим (css, js) контентом, а не с каким файлом обрабатывается фактическая конструкция ответа (HTML, XML и т.д.).

Ответ 1

Я собираюсь использовать настраиваемый view view, как это предлагается в комментариях. (и обновление моего приложения до Spring 3.0.0)

Ответ 2

У меня была та же проблема несколько месяцев назад!

В нашем мобильном проекте (используя Spring 2.5.6) мы закончили использование перехватчика с нашим SimpleUrlHandler. Это поймало все входящие запросы и добавило -m.jsp в конец любых мобильных запросов.

Он включал два шага:

1) объявление перехватчика для нашего стандартного URL-адреса:

 <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>

и 2), реализуя Interceptor, который искал слово "Mobile" в пользовательском агенте.

public class MultiViewController extends HandlerInterceptorAdapter {

Я рассказываю об этом более подробно в своем блоге (о новом захватывающем мире разработки мобильных веб-сайтов): http://plumnash.com/it/iphone-web-development-using-spring/

Ответ 3

Существует другой вариант, предложенный здесь

Однако я решил расширить ContentNegotiatingViewResolver и переопределить метод resolveViewName, я назвал свой ViewResolver HttpHeaderParamViewResolver. Расширенный метод выглядит примерно так:

@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);
}

Если headerParam = "User-Agent" (или любой другой параметр заголовка HTTp Header, который вам нравится, это определяется в bean xml), после этого вы его оцениваете и определяете имя viewName. В моем случае HttpHeaderParamViewResolver можно настроить с помощью карты, где ключ является префиксом, который должен быть добавлен к фактическому имени viewName, а значением является RegExp, который будет использоваться для оценки значения параметра заголовка. Это выглядит примерно так в 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>

Таким образом, если мой контроллер вызывает представление, называемое userDetails, и он обращается к приложению с IPhone, первый шаблон улавливает его и добавляет суффикс mobile-webkit, поэтому представление теперь является мобильным-webkit-userDetails, а затем передается в tilesViewResolver, который генерирует фактический вид.

Я исследовал множество возможностей, и я думаю, что это самый легкий и самый гибкий, с которым я смог придумать. В этом случае возможность выбора совершенно другого представления была критической, потому что мы поддерживаем широкий спектр пользовательских агентов: от WAP до IPhone 4 и мобильных телефонов, поддерживающих WebKit, так что представления сильно изменяются от пользовательского агента до пользовательского агента. Другим преимуществом является то, что вам больше не требуется обрабатывать эту проблему в представлении, поскольку вы можете иметь представления как специализированные, как вам нравится. Другая яркая сторона заключается в том, что вы можете реализовать это довольно легко, не удаляя или не изменяя разрешения представления, которые у вас уже есть, поскольку ContentNegotiationViewResolver имеет возможность делегировать вызов просмотра другим разрешениям представления в определенной последовательности, которую вы определить.

Нижняя сторона - это то, что у вас может возникнуть соблазн специализироваться на просмотрах и в итоге получить тонну файлов просмотра, что сделает приложение удобным кошмаром.

Надеюсь, что это будет полезно.

Ответ 4

Альтернативой, не требующей конфигурации в ViewResolver, может быть файл Velocity верхнего уровня, а затем условный синтаксический анализ подфайлов, который имеет примерно следующее.

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

Однако внедрение нового или расширение существующего ViewResolver - довольно простое решение и было бы таким, каким я бы с ним работал.