Подтвердить что ты не робот

Как настроить префикс URI по умолчанию для @RestController для всех контроллеров?

Я знаю, что вы можете установить server.contextPath в application.properties для изменения корневого контекста.

Кроме того, я могу добавить дополнительный контекст в конфигурацию приложения для Spring Boot, как в следующем примере (в Groovy), чтобы добавить "/api" к сопоставлениям URL-адреса корневого контекста:

@Bean
ServletRegistrationBean dispatcherServlet() {
     ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
        reg.name = "dispatcherServlet"
        reg.addInitParameter("contextConfigLocation", "")
        reg.addUrlMappings("/api/*")
        reg.loadOnStartup = 2
        reg
    }
}

Я пытаюсь создать отдельный базовый URI "/api" специально для вызовов веб-сервисов, которые я могу использовать для обеспечения безопасности и т.д. Однако использование вышеуказанного подхода будет означать, что любой из моих URI, веб-службы или нет, может быть достигнуто с помощью "/" или "/api" и не обеспечивает конкретной сегрегации.

Кто-нибудь знает лучший подход для установки базового пути для всех @RestController (s) с использованием конфигурации, без необходимости формально префикс каждого контроллера с помощью /api/? Если я вынужден вручную префикс URI для каждого контроллера, можно было бы ошибочно опустить это и обойти мои меры безопасности, специфичные для веб-служб.

Вот ссылка в Qaru на тот же тип вопроса, на который никогда не отвечали полностью:

Spring Загрузка: настройка префикса URL для RestControllers

4b9b3361

Ответ 1

Кто-то зарегистрировал проблему в Spring MVC Jira и придумал приятное решение, которое я сейчас использую. Идея состоит в том, чтобы использовать язык выражения Spring в префиксе, помещенном в каждый файл RestController, и ссылаться на одно свойство в файле Spring Boot application.properties.

Вот ссылка на проблему: https://jira.spring.io/browse/SPR-13882

Ответ 2

Появилось новое решение для решения этой проблемы, начиная с Spring Boot 1.4.0.RC1 (Подробнее см. https://github.com/spring-projects/spring-boot/issues/5004)

Решение Shahin ASkari отключает части конфигурации Auto, поэтому могут возникнуть другие проблемы.

Следующее решение использует его идею и правильно интегрирует ее в Spring boot. Для моего случая я хотел, чтобы все RestControllers были связаны с базовым путем api, но все равно служат статическому контенту с корневым путем (f.e. angular webapp)

Изменить. Я подвел итог в сообщении в блоге со слегка улучшенной версией. https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/ p >

@Configuration
public class WebConfig {

    @Bean
    public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
        return new WebMvcRegistrationsAdapter() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new RequestMappingHandlerMapping() {
                    private final static String API_BASE_PATH = "api";

                    @Override
                    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
                        Class<?> beanType = method.getDeclaringClass();
                        RestController restApiController = beanType.getAnnotation(RestController.class);
                        if (restApiController != null) {
                            PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
                                    .combine(mapping.getPatternsCondition());

                            mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
                                    mapping.getMethodsCondition(), mapping.getParamsCondition(),
                                    mapping.getHeadersCondition(), mapping.getConsumesCondition(),
                                    mapping.getProducesCondition(), mapping.getCustomCondition());
                        }

                        super.registerHandlerMethod(handler, method, mapping);
                    }
                };
            }
        };
    }

}

Ответ 3

У меня была такая же озабоченность и я не был поклонником опции Spring EL из-за проблем, о которых было документировано, и я хотел, чтобы префикс был жестко контролирован в контроллерах, но я не хотел зависеть от разработчиков, которые делают правильные вещь.

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

  • Определить пользовательскую аннотацию.
    Это позволяет разработчику явно предоставлять типизированные атрибуты, такие как int apiVersion(), String resourceName(). Эти значения позже станут основой префикса.
  • Аннотированные контроллеры останова с этой новой аннотацией
  • Реализован пользовательский шаблон RequestMappingHandlerMapping

В файле RequestMappingHandlerMapping я мог бы прочитать атрибут пользовательской аннотации и изменить окончательный RequestMappingInfo по мере необходимости. Вот несколько фрагментов кода:

@Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        return new MyCustomRequestMappingHandlerMapping();
    }
}

И в MyCustomRequestMappingHandlerMapping перезапишите registerHandlerMethod:

private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);

    public MyCustomRequestMappingHandlerMapping() {
        super();
    }

    @Override
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {

        // find the class declaring this method
        Class<?> beanType = method.getDeclaringClass();

        // check for the My rest controller annotation
        MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);

        if (myRestAnnotation != null) {
            // this is a My annotated rest service, lets modify the URL mapping 

            PatternsRequestCondition oldPattern = mapping.getPatternsCondition();

            // create a pattern such as /api/v${apiVersion}/${resourceName}
            String urlPattern = String.format("/api/v%d/%s", 
                    myRestAnnotation.apiVersion(), 
                    myRestAnnotation.resourceName());

            // create a new condition
            PatternsRequestCondition apiPattern = 
                    new PatternsRequestCondition(urlPattern);

            // ask our condition to be the core, but import all settinsg from the old 
            // pattern
            PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);

            myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}", 
                    beanType, myRestAnnotation, oldPattern, updatedFinalPattern);

            mapping = new RequestMappingInfo(
                    mapping.getName(),
                    updatedFinalPattern,
                    mapping.getMethodsCondition(),
                    mapping.getParamsCondition(),
                    mapping.getHeadersCondition(),
                    mapping.getConsumesCondition(),
                    mapping.getProducesCondition(),
                    mapping.getCustomCondition()
                    );
        }

        super.registerHandlerMethod(handler, method, mapping);
    }
}

Ответ 4

Немного менее подробное решение, которое не дублирует логику проверки аннотации, а только изменяет путь отображения:

private static final String API_PREFIX = "api";

@Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
    return new WebMvcRegistrationsAdapter() {
        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            return new RequestMappingHandlerMapping() {
                @Override
                protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
                    RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
                    if (mappingForMethod != null) {
                        return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
                    } else {
                        return null;
                    }
                }
            };
        }
    };
}

Побочные эффекты

Контроллер ошибок также будет отображаться в /api/error, что прерывает обработку ошибок (DispatcherServlet будет по-прежнему перенаправлять ошибки в/без префикса!).

Возможным решением является путь пропуска/ошибки при добавлении/api-префикса в вышеприведенный код (еще один "если" ).

Ответ 5

Также вы можете достичь того же результата, настроив WebMVC следующим образом:

@Configuration
public class PluginConfig implements WebMvcConfigurer {

public static final String PREFIX = "/myprefix";

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCistomAnnotation.class));
}

}

  1. WebMvcConfigurer в любом классе @Configuration.
  2. Переопределите метод configurePathMatch.
  3. С PathMatchConfigurer вы можете сделать много полезных вещей, например добавить префикс для нескольких классов, которые удовлетворяют условиям предиката.