Попытка создать УСПОКОИТЕЛЬНЫЕ URL с несколькими точками в части “имени файла” - Spring 3.0 MVC

Я использую Spring MVC (3.0) с управляемыми аннотацией контроллерами. Я хотел бы создать УСПОКОИТЕЛЬНЫЕ URL для ресурсов и смочь не потребовать (но все еще дополнительно позволить) расширение файла на конце URL (но предположил бы тип содержимого HTML если никакое расширение). Это работает out-of-the-box с Spring MVC, пока нет никаких точек (период/точка) в части имени файла.

Однако некоторые мои URL требуют идентификатора с точками на имя. Например, как это:

http://company.com/widgets/123.456.789.500

В этом случае Spring ищет тип контента для расширения .500 и не находит ни один так ошибками. Я могу использовать обходные решения как добавление .html в конец, кодируя идентификатор или добавляя запаздывающую наклонную черту. Я не доволен никем, если они, но могли бы, вероятно, жить с добавлением .html.

Я неудачно искал способ переопределить обнаружение расширения файла по умолчанию в Spring.

Действительно ли возможно настроить или отключить обнаружение расширения файла для данного метода контроллера или шаблона URL и т.д.?

15
задан Cœur 23 July 2017 в 10:15
поделиться

3 ответа

@PathVariable Сопоставление рисунка - это немного дергается, когда речь идет о точках в URL (см. SPR-5778 ). Вы можете сделать его менее Thitchy (но более воспитанным) и лучше контролировать URL-адреса, установив свойство USEFAULTSUFFIXPattern на по умолчаниюнотационному отключению на false .

Если вы еще не были явно объявлены DEALKNANDATIONATIONATIONLERMARCE в вашем контексте (и большинство людей не так, как он неявен неявно для вас), то вы можете добавить его прямо и установить это свойство.

12
ответ дан 1 December 2019 в 00:59
поделиться

Я не утверждаю, что у меня есть окончательный ответ на все это (это довольно открытый вопрос, который вы должны попытаться разбить на более мелкие части, и это зависит от ваших фактических требований, на самом деле я искушен проголосовать за его закрытие), но я прокомментирую несколько вещей:

  1. Я бы забыл о моделировании этого на RDBMS. Фасетный поиск просто не работает в реляционной схеме .
  2. ИМО не является подходящим местом для генерации кода. Следует разработать код таким образом, чтобы он не изменялся с изменениями данных (я не говорю об изменениях схемы ).
  3. Сохранение метаданных/атрибутов в электронной таблице Excel кажется очень плохой идеей. Я бы построил пользовательский интерфейс для редактирования этого, который будет храниться на Solr/MongoDB/CouchDB/все, что вы выберете для управления этим.
  4. Solr не «просто зеркально отражает реляционную БД». Фактически Solr полностью не зависит от реляционных баз данных. Одним из наиболее распространенных случаев является сброс данных из RDBMS в Solr (денормализация данных в процессе), но Solr является достаточно гибким для работы без какого-либо реляционного источника данных.
  5. Иерархическое фасетирование в Solr все еще остается открытой проблемой в исследованиях. В настоящее время изучаются два отдельных подхода ( SOLR-64 , SOLR-792 )
-121--3747484-

Для работы некоторых приложений, таких как BetterKeyboard , необходимо изменить некоторые системные параметры настройки. Вместо того, чтобы изменять его сами, они имеют кнопку, которая отправляет пользователя в точное окно, где информация должна быть изменена. Я думаю, что это сделано с намерением . На твоем месте я бы попытался этого добиться. Если пользователь хочет изменить свой языковой стандарт, отправьте его в настройки языкового стандарта своего телефона.

-121--3391018-

Вероятно, это уродливый взлом, я просто хотел изучить расширяемость Spring @ MVC. Вот настраиваемый SunMatcher . Он использует $ в образец в качестве конечного маркера - если образец заканчивается с ним, маркер удаляется и образец сопоставляется с сопоставителем по умолчанию, но если образец имеет $ посередине (например, ... $. * ), такой образец не сопоставляется.

public class CustomPathMatcher implements PathMatcher {
    private PathMatcher target;

    public CustomPathMatcher() {
        target = new AntPathMatcher();
    }

    public String combine(String pattern1, String pattern2) {
        return target.combine(pattern1, pattern2); 
    }

    public String extractPathWithinPattern(String pattern, String path) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return "";
        }
        return target.extractPathWithinPattern(pattern, path);
    }

    public Map<String, String> extractUriTemplateVariables(String pattern,
            String path) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return Collections.emptyMap();
        }
        return target.extractUriTemplateVariables(pattern, path);
    }

    public Comparator<String> getPatternComparator(String pattern) {
        final Comparator<String> targetComparator = target.getPatternComparator(pattern);
        return new Comparator<String>() {
            public int compare(String o1, String o2) {
                if (isEncoded(o1)) {
                    if (isEncoded(o2)) {
                        return 0;
                    } else {
                        return -1;
                    }
                } else if (isEncoded(o2)) {
                    return 1;
                }
                return targetComparator.compare(o1, o2);
            }        
        };
    }

    public boolean isPattern(String pattern) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return true;
        }
        return target.isPattern(pattern);
    }

    public boolean match(String pattern, String path) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return false;
        }
        return target.match(pattern, path);
    }

    public boolean matchStart(String pattern, String path) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return false;
        }
        return target.match(pattern, path);
    }

    private boolean isEncoded(String pattern) {
        return pattern != null && pattern.contains("$");
    }

    private String resolvePattern(String pattern) {
        int i = pattern.indexOf('$');
        if (i < 0) return pattern;
        else if (i == pattern.length() - 1) {
            return pattern.substring(0, i);
        } else {
            String tail = pattern.substring(i + 1);
            if (tail.startsWith(".")) return null;
            else return pattern.substring(0, i) + tail;
        }
    }
}

Config:

<bean id = "pathMatcher" class = "sample.CustomPathMatcher" />

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name = "pathMatcher" ref="pathMatcher" />
</bean>

<bean class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name = "pathMatcher" ref="pathMatcher" />
</bean>

И использование (задано «/hello/1.2.3 », значение -« 1,2,3 »):

@RequestMapping(value = "/hello/{value}$", method = RequestMethod.GET)
public String hello(@PathVariable("value") String value, ModelMap model)

EDIT: : Теперь не нарушается правило« не имеет значения »

12
ответ дан 1 December 2019 в 00:59
поделиться

У меня была та же проблема, и я также решил ее с помощью настраиваемого PathMatcher. Мое решение несколько проще того, что предлагал axtavt. Мой PathMatcher также имеет частную конечную цель AntPathMatcher, и он делегирует все вызовы ему без изменений, за исключением метода match ():

@Override
public boolean match(String pattern, String path) {
    return pattern.endsWith(".*") ? false : target.match(pattern, path);
}

Это работает, потому что Spring пытается сопоставить контроллеры, добавляя «. » в конец. Например, с сопоставлением путей "/ widgets / {id}" и URL "/widgets/1.2.3.4" Spring пытается сначала сопоставить "/ widgets / {id}. ", а затем "/ widgets / { я бы}". Первый будет соответствовать, но для id останется только «1.2.3».

My PatchMatcher специально отклоняет шаблоны, заканчивающиеся на ". *", Поэтому первая попытка не удалась, а вторая соответствует.

Если вы используете ContentNegotiatingViewResolver, вы все равно можете указать тип контента в URL-адресах, используя параметр запроса «формат» (если для параметра FavorParameter установлено значение true).

-jarppe

3
ответ дан 1 December 2019 в 00:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: