62

У меня есть следующий код в одном из моих контроллеров:Как избежать «кругового обзора путь» исключение с тест Spring MVC

@Controller 
@RequestMapping("/preference") 
public class PreferenceController { 


    @RequestMapping(method = RequestMethod.GET, produces = "text/html") 
    public String preference() { 
     return "preference"; 
    } 

Я просто пытаюсь проверить его с помощью теста Spring MVC следующим образом :

@ContextConfiguration 
@WebAppConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
public class PreferenceControllerTest { 

    @Autowired 
    private WebApplicationContext ctx; 

    private MockMvc mockMvc; 
    @Before 
    public void setup() { 
     mockMvc = webAppContextSetup(ctx).build(); 
     } 

    @Test 
    public void circularViewPathIssue() throws Exception { 
     mockMvc.perform(get("/preference"))// 
       .andDo(print()); 
    } 

Я получаю следующее исключение:

круговой путь вид [предпочтение]: снова вернется к текущему URL-адресу обработчика [/ предпочтения]. Проверьте настройку ViewResolver! (Подсказка: Это может быть результат неустановленного зрения, из-за вид по умолчанию поколения имени.)

Что я нахожу странным, что он отлично работает, когда я загрузить «полную» конфигурацию контекста что включает в себя шаблон и вид арбитр, как показано ниже:

<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="webTemplateResolver"> 
     <property name="prefix" value="WEB-INF/web-templates/" /> 
     <property name="suffix" value=".html" /> 
     <property name="templateMode" value="HTML5" /> 
     <property name="characterEncoding" value="UTF-8" /> 
     <property name="order" value="2" /> 
     <property name="cacheable" value="false" /> 
    </bean> 

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

Но как я должен тестировать свое приложение с помощью теста Spring MVC? Кто-нибудь понял?

+1

Можете ли вы разместить «ViewResolver», который вы используете, когда он не работает? –

+0

@SotiriosDelimanolis: Я не уверен, что какой-либо viewResolver используется Spring MVC Test. [документация] (http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework) – balteo

+4

У меня была та же проблема, но проблема была в том, что я не добавил ниже зависимости. org.springframework.boot весна-загрузка-стартер-thymeleaf aamir

ответ

40

Это не имеет никакого отношения к тестированию Spring MVC.

Когда вы не объявляете ViewResolver, Spring регистрирует по умолчанию InternalResourceViewResolver, который создает экземпляры JstlView для оказания View.

JstlView класс расширяет InternalResourceView который

Обертка для JSP или другого ресурса в пределах того же веб-приложения. Показывает объекты модели в качестве атрибутов запроса и перенаправляет запрос на указанный URL ресурса с помощью javax.servlet.RequestDispatcher.

URL-адрес для этой точки зрения предполагается определить ресурс в приложении веб , подходит для RequestDispatcher Форвард или включить метод.

Полужирный является моим. В других словах представление перед рендерингом попытается получить RequestDispatcher, к которому относится forward(). Перед этим он проверяет следующее

if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) { 
    throw new ServletException("Circular view path [" + path + "]: would dispatch back " + 
         "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " + 
         "(Hint: This may be the result of an unspecified view, due to default view name generation.)"); 
} 

где path это имя вид, что вы вернулись из @Controller. В этом примере это preference. Переменная uri содержит uri обрабатываемого запроса, который равен /context/preference.

В приведенном выше коде понимается, что если бы вы перешли на /context/preference, тот же сервлет (так же, как и предыдущий) обработал бы запрос, и вы отправились бы в бесконечный цикл.


Когда вы объявляете ThymeleafViewResolver и ServletContextTemplateResolver с конкретным prefix и suffix, он строит View по-разному, придавая ему путь, как

WEB-INF/web-templates/preference.html 

ThymeleafView экземпляры найдите файл относительно ServletContext пути используя ServletContextResourceResolver

templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);` 

, который в конечном счете

return servletContext.getResourceAsStream(resourceName); 

Это получает ресурс, который по отношению к ServletContext пути. Затем он может использовать TemplateEngine для генерации HTML. Здесь нет никакого бесконечного цикла.

+1

Спасибо за ваш подробный ответ. Я понимаю, почему цикл не возникает, когда я использую Thymeleaf и почему это происходит, когда я не использую Thymeleaf view resolver. Тем не менее, я все еще не уверен, как изменить свой конфиг, чтобы я мог тестировать свое приложение ... – balteo

+1

@balteo Когда вы используете 'ThymleafViewResolver',' View' разрешен как файл относительно 'prefix' и' suffix' Вы предоставляете. Если вы не используете это решение, Spring использует по умолчанию 'InternalResourceViewResolver', который находит ресурсы с помощью' 'RequestDispatcher' '(http://docs.oracle.com/javaee/6/api/javax/servlet/RequestDispatcher.html). Этот ресурс может быть «Servlet». В этом случае это потому, что путь '/ preference' сопоставляется с вашим' DispatcherServlet'. –

+2

@balteo Чтобы протестировать ваше приложение, предоставьте правильный «ViewResolver». Либо «ThymeleafViewResolver», как в вашем вопросе, ваш собственный настроенный «InternalResourceViewResolver» или измените имя представления, которое вы возвращаете в своем контроллере. –

61

Я решил эту проблему с помощью @ResponseBody, как показано ниже:

@RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"}) 
    @ResponseStatus(HttpStatus.OK) 
    @Transactional(value = "jpaTransactionManager") 
    public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) { 
+7

Они хотят вернуть HTML, разрешив представление, а не возвратить сериализованную версию «List ». –

+0

Это разрешило мою проблему, возвращая ответ JSON для веб-службы Spring rest. – Joe

+0

Приятно, если я не укажу, что производит = {"application/json"}, все равно работает. Производит ли он json по умолчанию? – Jay

26

Вот как я решил эту проблему:

@Before 
    public void setup() { 
     InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 
     viewResolver.setPrefix("/WEB-INF/jsp/view/"); 
     viewResolver.setSuffix(".jsp"); 

     mockMvc = MockMvcBuilders.standaloneSetup(new HelpController()) 
           .setViewResolvers(viewResolver) 
           .build(); 
    } 
+0

Это только для тестовых ящиков. Не для контроллеров. – cst1992

+1

Помог кому-то устранить эту проблему в одном из своих новых модульных тестов, это именно то, что мы искали. – Bradford2000

+0

Я использовал это, но, несмотря на то, что у меня был неправильный префикс и суффикс для моего резольвера, это сработало. Можете ли вы объяснить, почему это необходимо? – dushyantashu

9

Здесь легко исправить, если вы на самом деле не заботиться об оказании вид.

Создайте подкласс InternalResourceViewResolver, который не проверяет для круговых траекторий Вида:

public class StandaloneMvcTestViewResolver extends InternalResourceViewResolver { 

    public StandaloneMvcTestViewResolver() { 
     super(); 
    } 

    @Override 
    protected AbstractUrlBasedView buildView(final String viewName) throws Exception { 
     final InternalResourceView view = (InternalResourceView) super.buildView(viewName); 
     // prevent checking for circular view paths 
     view.setPreventDispatchLoop(false); 
     return view; 
    } 
} 

Затем установите тест с ним:

MockMvc mockMvc; 

@Before 
public void setUp() { 
    final MyController controller = new MyController(); 

    mockMvc = 
      MockMvcBuilders.standaloneSetup(controller) 
        .setViewResolvers(new StandaloneMvcTestViewResolver()) 
        .build(); 
} 
+0

Это исправило мою проблему. Я просто добавил класс StandaloneMvcTestViewResolver в тот же каталог тестов и использовал его в MockMvcBuilders, как описано выше. Спасибо –

+0

У меня была такая же проблема, и это исправило это и для меня. Большое спасибо! – Johan

0

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

@Configuration 
@EnableWebMvc 
@ComponentScan(basePackages = { "com.example.springmvc" }) 
public class WebMvcConfig extends WebMvcConfigurerAdapter { 

    @Bean 
    public InternalResourceViewResolver internalResourceViewResolver() { 
     InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 
     resolver.setPrefix("/jsp/"); 
     resolver.setSuffix(".jsp"); 
     return resolver; 
    } 
} 
+0

Спасибо, что это отлично работает для меня. Мое приложение сломалось после обновления до весенней загрузки 1.3.1 с 1.2.7 и была только эта строка, которая была неудачной registry.addViewController ("/ login"). SetViewName ("login"); При регистрации этого компонента приложение снова работало ... по крайней мере, логин пошел wll. – le0diaz

-2

Другой простой подход:

package org.yourpackagename; 

import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.boot.builder.SpringApplicationBuilder; 
import org.springframework.boot.context.web.SpringBootServletInitializer; 

@SpringBootApplication 
public class Application extends SpringBootServletInitializer { 

     @Override 
     protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 
      return application.sources(PreferenceController.class); 
     } 


    public static void main(String[] args) { 
     SpringApplication.run(PreferenceController.class, args); 
    } 
} 
2

Для Thymeleaf:

Я только начал использовать пружину 4 и thymeleaf, когда я столкнулся с этой ошибки была решена путем добавления:

<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> 
    <property name="templateEngine" ref="templateEngine" /> 
    <property name="order" value="0" /> 
</bean> 
1

При использовании аннотации @Controller вам нужны @RequestMapping и @ResponseBody аннотации. Повторите попытку после добавления аннотации @ResponseBody

2

Если вы используете Spring Boot, добавьте зависимость тимелеафа в ваш pom.XML:

<dependency> 
     <groupId>org.thymeleaf</groupId> 
     <artifactId>thymeleaf-spring4</artifactId> 
     <version>2.1.6.RELEASE</version> 
    </dependency> 
2

Я использую Spring бутс, чтобы попытаться загрузить веб-страницу, а не тест, и имел эту проблему. Мое решение было немного иным, чем те, которые были рассмотрены с учетом немного разных обстоятельств. (Хотя эти ответы helpled меня понять.)

Я просто должен был изменить Spring загрузки стартера зависимость в Maven от:

<dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
</dependency> 

к:

<dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-thymeleaf</artifactId> 
</dependency> 

Просто изменяя 'Интернет' для «тимелеафа» исправил проблему для меня.

0

Это происходит потому, что Spring удаляет «предпочтение» и добавляет «предпочтение» снова, делая тот же путь, что и запрос Uri.

Случается так: URI запроса: "/ предпочтение"

REMOVE "предпочтение": "/"

Append путь: "/" + "предпочтение"

конец string: "/ предпочтение"

Это попадает в цикл, который Spring уведомляет вас, бросая исключение.

Лучшее в ваших интересах, чтобы дать другое название вида «preferenceView» или что угодно.

Смежные вопросы