Как избежать исключения "путь кругового обзора" с помощью теста 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? У кого-нибудь есть подсказка?

861   14  

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 путь с помощью ServletContextResourceResolver

templateInputStream = 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, проблема решена путем добавления InternalResourceViewResolver bean к конфигурации. Надеюсь, это будет полезно.

@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

    Ничего не найдено.