Как реализовать пользовательские свойства @RequestMapping

Как пример, выберите отображение поддоменов.

Эта статья: Управление несколькими доменами и субдоменами в Google App Engine для того же приложения рекомендует разрешать субдомен в фильтре и назначать переменную заголовкам ServletRequest.

Тогда отображение будет выглядеть так:

@RequestMapping(value = "/path", headers="subdomain=www")
 public String subsiteIndexPage(Model model,HttpServletRequest request) { ... }

Если мы хотим создать собственное свойство @RequestMapping, например субдомен, например. для создания отображения следующим образом:

@RequestMapping(value = "/some/action", subdomain = "www")
public String handlerFunction(){ ... }

мы должны переопределить определение @RequestMapping @interface и переопределить методы RequestMappingHandlerMapping, с нашей собственной реализацией
(как указано в JIRA: " Разрешить условия сопоставления пользовательских запросов SPR-7812" ).

Правильно ли это? Может ли кто-нибудь дать подсказку, как достичь этой функциональности?


Идея 1:
Как было предложено в исходном потоке jira, необходимо создать собственную реализацию RequestCondition

Существует проект, который использует это решение для github: https://github.com/rstoyanchev/spring-mvc-31-demo/

И связанный вопрос SO: Добавление пользовательского RequestCondition в Spring mvc 3.1

Возможно, отображение типа @Subdomain("www") для типа и метода, возможно решение?


Ссылка на тот же вопрос на forum.springsource.com

Ответ 1

Я создал решение на основе ссылки spring-mvc-31-demo

Это решение можно использовать для отображения только одного RequestCondition на данный момент. Я создал два вопроса для уведомления, это должно быть изменено:
https://github.com/rstoyanchev/spring-mvc-31-demo/issues/5
https://jira.springsource.org/browse/SPR-9350

Это решение использует пользовательскую функцию @RequestCondition для платформы Spring 3.1.1.RELEASE

ИСПОЛЬЗОВАНИЕ

Пример 1:

@Controller
@SubdomainMapping(value = "subdomain", tld = ".mydomain.com")
class MyController1 {
    // Code here will be executed only on address match:
    // subdomain.mydomain.com
}

Пример 2:

@Controller
class MyController2 {

    @RequestMapping("/index.html")
    @SubdomainMapping("www")
    public function index_www(Map<Object, String> map){
        // on www.domain.com
        // where ".domain.com" is defined in SubdomainMapping.java
    }

    @RequestMapping("/index.html")
    @SubdomainMapping("custom")
    public function index_custom(Map<Object, String> map){
        // on custom.domain.com
        // where ".domain.com" is defined in SubdomainMapping.java
    }
}

Нам нужны три файла

  • SubdomainMapping.java
  • SubdomainRequestCondition.java
  • SubdomainRequestMappingHandlerMapping.java

SubdomainMapping.java

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SubdomainMapping {

    /**
    * This param defines single or multiple subdomain
    * Where the Method/Type is valid to be called
    */
    String[] value() default {};
    /**
    * This param defines site domain and tld
    * It important to put the leading dot
    * Not an array, so cannot be used for mapping multiple domains/tld
    */
    String tld() default ".custom.tld";
}

SubdomainRequestCondition.java

import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.mvc.condition.RequestCondition;

public class SubdomainRequestCondition implements
        RequestCondition<SubdomainRequestCondition> {

    private final Set<String> subdomains;
    private final String tld;

    public SubdomainRequestCondition(String tld, String... subdomains) {
        this(tld, Arrays.asList(subdomains));
    }

    public SubdomainRequestCondition(String tld, Collection<String> subdomains) {
        this.subdomains = Collections.unmodifiableSet(new HashSet<String>(
                subdomains));
        this.tld = tld;
    }

    @Override
    public SubdomainRequestCondition combine(SubdomainRequestCondition other) {
        Set<String> allRoles = new LinkedHashSet<String>(this.subdomains);
        allRoles.addAll(other.subdomains);
        return new SubdomainRequestCondition(tld, allRoles);
    }

    @Override
    public SubdomainRequestCondition getMatchingCondition(
            HttpServletRequest request) {
        try {
            URL uri = new URL(request.getRequestURL().toString());
            String[] parts = uri.getHost().split(this.tld);
            if (parts.length == 1) {
                for (String s : this.subdomains) {
                    if (s.equalsIgnoreCase(parts[0])) {
                        return this;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        return null;
    }

    @Override
    public int compareTo(SubdomainRequestCondition other,
            HttpServletRequest request) {
        return org.apache.commons.collections.CollectionUtils.removeAll(other.subdomains, this.subdomains).size();
    }

}

SubdomainRequestMappingHandlerMapping.java

import java.lang.reflect.Method;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

public class CustomRequestMappingHandlerMapping extends
        RequestMappingHandlerMapping {

    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        SubdomainMapping typeAnnotation = AnnotationUtils.findAnnotation(
                handlerType, SubdomainMapping.class);
        return createCondition(typeAnnotation);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        SubdomainMapping methodAnnotation = AnnotationUtils.findAnnotation(
                method, SubdomainMapping.class);
        return createCondition(methodAnnotation);
    }

    private RequestCondition<?> createCondition(SubdomainMapping accessMapping) {
        return (accessMapping != null) ? new SubdomainRequestCondition(
                accessMapping.tld(), accessMapping.value()) : null;
    }

}

Переустановленное

ВАЖНО: До сих пор невозможно использовать это решение с XML-элементом
< mvc: управляемый аннотацией > , см. JIRA https://jira.springsource.org/browse/SPR-9344 для объяснения

  • Вам необходимо зарегистрировать пользовательский MappingHandler bean, указав на эту специальную реализацию SubdomainRequestMappingHandlerMapping class
  • Вы должны установить его порядок ниже, чем по умолчанию RequestMappingHandlerMapping
    ИЛИ
    Замените зарегистрированный RequestMappingHandlerMapping (возможно, на порядок = 0)

Подробнее об этом решении см. в соответствующем проекте github

Ответ 2

Это правильно, но это было бы слишком сложно. Вам лучше проверить заголовок Host, содержит ли он данный поддомен.

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