Spring MVC Matrix Variables извлекает только одно значение параметра

Я создаю приложение API для отдыха spring-mvc и намерен использовать матричные переменные для некоторых своих конечных точек. К сожалению, я не могу получить более одного значения для каждой используемой матричной переменной.

Моя версия spring-mvc — spring-webmvc:4.3.12.RELEASE.

Я выполнил шаги, показанные в этом примере реализации: http://www.baeldung.com/spring-mvc-matrix-variables.

Я включил матричные переменные Spring MVC:

package fr.compagny.project.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.util.UrlPathHelper;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

Итак, я создал 2 тестовые конечные точки:

package fr.compagny.project.webservice;

import [...]

@Api
@RefreshScope
@RestController
@RequestMapping(value = "/my_awesome_project")
public class ProjectWS {

    //Services
    private ProjectService projectService;

    //Validator
    private ValidatorService validator;

    @ApiOperation(value = "Matrix Variable Test 1.")
    @GetMapping(value = "/matrix_test_one/{vars}", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public String getMatrixTestOne (@MatrixVariable(pathVar = "vars", required = true) String v1,
                                    @MatrixVariable(pathVar = "vars", required = true) String v2,
                                    @MatrixVariable(pathVar = "vars", required = true) String v3) {
        return v1 + v2 + v3;
    }

    @ApiOperation(value = "Matrix Variable Test 2.")
    @GetMapping(value = "/matrix_test_two/{vars}", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public Map<String, String> getMatrixTestTwo (@MatrixVariable Map<String, String> vars) {
        return vars;
    }

    @Autowired
    public void setProjectService(ProjectService projectService) {
        this.projectService = projectService;
    }

    @Autowired
    public void setValidatorService(ValidatorService validatorService) {
        this.validator = validatorService;
    }
}

Когда я звоню

GET http://[...]/my_awesome_project/matrix_test_one/v1=toto;v2=titi;v3=tata
OR
GET http://[...]/my_awesome_project/matrix_test_one/v1=toto

У меня такое же следующее сообщение об ошибке:

Произошла непредвиденная ошибка (тип = неверный запрос, статус = 400). Отсутствует матричная переменная 'v2' для параметра метода типа String

Но когда я звоню

GET http://[...]/my_awesome_project/matrix_test_one/v2=titi
OR
GET http://[...]/my_awesome_project/matrix_test_one/[anything except "v1=*"]

У меня такое же следующее сообщение об ошибке:

Произошла непредвиденная ошибка (тип = неверный запрос, статус = 400). Отсутствует матричная переменная 'v1' для параметра метода типа String

Таким образом, Spring выглядит нормально, чтобы получить первый элемент матричной переменной, но тогда остановитесь.

Поэтому я продолжаю пробовать вторую тестовую функцию:

GET http://[...]/my_awesome_project/matrix_test_two/v1=toto;v2=titi;v3=tata
OR
GET http://[...]/my_awesome_project/matrix_test_two/v1=toto

Возврат :

{
    "v1": "toto"
}

-

GET http://[...]/my_awesome_project/matrix_test_two/v2=titi;v1=toto;v3=tata

Возврат :

{
    "v2": "titi"
}

Так что такое поведение, похоже, подтверждает мои опасения.

Вы видели что-то, что я пропустил, чтобы включить поддержку матричных переменных (возможно, связанных с точкой с запятой)?


person Josef    schedule 31.05.2018    source источник


Ответы (1)


В упомянутом примере используется Spring Boot. Запуск примера через Spring Boot работает, как и ожидалось. Без Spring Boot он не работает из коробки, как объясняется в этом Вопросы и ответы. Причина в том, что UrlPathHelper, введенный из @Configuration (точка 2 в примере), не используется для обработки запроса. Используется экземпляр UrlPathHelper по умолчанию, поэтому urlPathHelper.shouldRemoveSemicolonContent() возвращает true. Это удаляет матричные переменные из запроса.

РЕДАКТИРОВАТЬ:

Я отладил его и оказалось, что есть два бина типа RequestMappingHandlerMapping.

Два bean-компонента типа RequestMappingHandlerMapping

Итак, я попробовал эту конфигурацию:

@Configuration
@ComponentScan(basePackageClasses = { WebMvcConfiguration.class })
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Bean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")
    @Qualifier("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")
    public RequestMappingHandlerMapping fullyQualifiedRequestMappingHandlerMapping() {
        return requestMappingHandlerMapping();
    }

    @Bean
    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
        requestMappingHandlerMapping.getUrlPathHelper().setRemoveSemicolonContent(false);
        return requestMappingHandlerMapping;
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = configurer.getUrlPathHelper();
        if (urlPathHelper == null) {
            urlPathHelper = new UrlPathHelper();
        }
        urlPathHelper.setRemoveSemicolonContent(false);
    }

}

Но полностью квалифицированный компонент не был создан первым методом. Этот компонент обрабатывает запрос. Таким образом, матричные переменные все же были удалены.

Поскольку я не смог предоставить фабричный метод для bean-компонента, я попытался изменить состояние bean-компонента:

@Component
public class Initializer {

    @Autowired
    private ApplicationContext appContext;

    @PostConstruct
    protected void init() {
        initUrlPathHelper("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
        initUrlPathHelper("requestMappingHandlerMapping");
    }

    protected void initUrlPathHelper(String beanName) {
        AbstractHandlerMapping b = (AbstractHandlerMapping) appContext.getBean(beanName);
        b.setUrlPathHelper(urlPathHelper());
    }

    public UrlPathHelper urlPathHelper() {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        return urlPathHelper;
    }

}

Это сделало это для меня. Переменные матрицы сопоставлены.

person LuCio    schedule 31.05.2018
comment
Я только что изменил конечную точку matrix_test_one, чтобы проверить это. Я сталкиваюсь с точно таким же поведением, описанным в моем первоначальном посте. Извини. - person Josef; 31.05.2018
comment
Я настроил ваш контроллер и воспроизвел случай. Я нашел аналогичный SO-Q&A. Но руководство, которое вы используете, устанавливает urlPathHelper.setRemoveSemicolonContent(false). Так что у меня до сих пор нет решения. - person LuCio; 31.05.2018
comment
Я вернулся к руководству baeldung.com. Они используют Spring Boot. Поэтому я изменил код для запуска через Spring Boot, и это сработало. Я обновляю свой ответ. - person LuCio; 01.06.2018