Как избежать исключения "путь кругового обзора" с помощью теста Spring MVC
у меня есть следующий код в один из моих контроллеров:
@Controller
@RequestMapping("/preference")
public class PreferenceController {
@RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String preference() {
return "preference";
}
}
Я просто пытаюсь проверить его с помощью Spring MVC test следующим образом:
@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 обработчика [/preference] снова. Проверьте настройки 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 test? У кого-нибудь есть подсказка?
14 ответов:
это не имеет ничего общего с весенним тестированием MVC.
когда вы не объявляете
ViewResolver, Весна регистрирует значение по умолчаниюInternalResourceViewResolver, который создает экземплярыJstlViewна оказаниеView.The
JstlViewкласс расширяетсяInternalResourceViewчто этооболочка для JSP или другого ресурса в том же веб-приложении. Предоставляет объекты модели в качестве атрибутов запроса и пересылает запрос на указанный URL-адрес ресурса с использованием класса javax.сервлет.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путь с помощьюServletContextResourceResolvertemplateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`что в итоге
return servletContext.getResourceAsStream(resourceName);это получает ресурс, который относительно
ServletContextпуть. Затем он может использоватьTemplateEngineдля создания HTML. Нет никакого способа, бесконечный цикл может произойти здесь.
Я решил эту проблему с помощью @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) {
вот как я решил эту проблему:
@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(); }
вот простое решение, если вы на самом деле не заботитесь о визуализации представления.
создайте подкласс 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(); }
@Controller→@RestControllerу меня была та же проблема, и я заметил, что мой контроллер тоже был примечен с
@Controller. Заменив его на@RestControllerрешается вопрос. Вот объяснение от Spring Web MVC:@RestController-это составленная аннотация, которая сама является мета-аннотированной с @Controller и @ResponseBody, указывающими на контроллер, каждый из которых метод наследует аннотацию @ResponseBody уровня типа и поэтому пишет непосредственно к телу ответа против разрешения и рендеринга представления с помощью шаблона HTML.
Если вы используете Spring Boot, добавьте зависимость thymeleaf в свой pom.XML-код:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>2.1.6.RELEASE</version> </dependency>
Я использую Spring Boot, чтобы попытаться загрузить веб-страницу, а не тест, и у меня была эта проблема. Мое решение было немного другим, чем те, что выше, учитывая немного другие обстоятельства. (хотя эти ответы помогли мне понять.)
мне просто пришлось изменить свою зависимость от Spring Boot starter в Maven от кого:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>to:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>просто изменив ' web ' на 'thymeleaf' Исправлена проблема для меня.
Для Thymeleaf:
Я только начал использовать spring 4 и thymeleaf, когда я столкнулся с этой ошибкой, она была решена путем добавления:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="order" value="0" /> </bean>
при использовании
@Controllerаннотацию, вам нужно@RequestMappingи@ResponseBodyПримечание. Повторите попытку после добавления аннотации@ResponseBody
Я использую аннотацию для настройки spring web app, проблема решена путем добавления
InternalResourceViewResolverbean к конфигурации. Надеюсь, это будет полезно.@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; } }
Это происходит потому, что Spring удаляет " предпочтение "и добавляет" предпочтение " снова, делая тот же путь, что и Uri запроса.
происходит подобное : запрос Uri: "/preference"
удалить "предпочтения": "/ "
добавить путь: "/"+"предпочтение"
конец строки: "/preference"
Это попадание в цикл, который Весна уведомляет вас, бросая исключение.
его лучше всего в ваших интересах, чтобы дать другое имя представления, как "preferenceView" или все, что вам нравится.
Я использую Spring Boot с Thymeleaf. Это то, что сработало для меня. Есть похожие ответы с JSP, но обратите внимание, что я использую HTML, а не JSP, и они находятся в папке
src/main/resources/templatesкак в стандартном проекте Spring Boot, как объяснено здесь. Это также может быть ваш случай.@InjectMocks private MyController myController; @Before public void setup() { MockitoAnnotations.initMocks(this); this.mockMvc = MockMvcBuilders.standaloneSetup(myController) .setViewResolvers(viewResolver()) .build(); } private ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("classpath:templates/"); viewResolver.setSuffix(".html"); return viewResolver; }надеюсь, что это помогает.
попробуйте добавить compile ("org.springframework.boot: spring-boot-starter-thymeleaf") зависимость от вашего файла gradle.Thymeleaf помогает отображать представления.
еще один простой подход:
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); } }
Comments