【问题标题】:Spring 3.0 MVC binding Enums Case SensitiveSpring 3.0 MVC 绑定枚举区分大小写
【发布时间】:2011-01-06 16:22:35
【问题描述】:

如果我在这样的 Spring 控制器中有一个 RequestMapping...

@RequestMapping(method = RequestMethod.GET, value = "{product}")
public ModelAndView getPage(@PathVariable Product product)

Product 是一个枚举。例如。产品.首页

当我请求页面时,mysite.com/home

我明白了

Unable to convert value "home" from type 'java.lang.String' to type 'domain.model.product.Product'; nested exception is java.lang.IllegalArgumentException: No enum const class domain.model.product.Product.home

有没有办法让枚举类型转换器理解小写的 home 实际上是 Home?

我想保持 url 不区分大小写,并且我的 Java 枚举使用标准大写字母。

谢谢

解决方案

public class ProductEnumConverter extends PropertyEditorSupport
{
    @Override public void setAsText(final String text) throws IllegalArgumentException
    {
        setValue(Product.valueOf(WordUtils.capitalizeFully(text.trim())));
    }
}

注册

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="domain.model.product.Product" value="domain.infrastructure.ProductEnumConverter"/>
            </map>
        </property>
    </bean>

添加到需要特殊转换的控制器

@InitBinder
public void initBinder(WebDataBinder binder)
{
    binder.registerCustomEditor(Product.class, new ProductEnumConverter());
} 

【问题讨论】:

标签: java spring spring-mvc


【解决方案1】:

一般来说,您想创建一个新的 PropertyEditor 来为您进行规范化,然后像这样在 Controller 中注册它:

@InitBinder
 public void initBinder(WebDataBinder binder) {

  binder.registerCustomEditor(Product.class,
    new CaseInsensitivePropertyEditor());
 }

【讨论】:

  • 不想把这个添加到每个使用这个枚举的控制器上。是否有全球解决方案?
  • @dom farr: 有,但看起来 skaffman 已经打败了我。看看他的回答。
  • 对于 Spring 3.2 用户;您现在可以使用@ControllerAdvice 注册全局初始化绑定器(除其他外)。请参阅the reference guide 了解更多信息。
  • @GaryF 所以我们不需要创建一个 CustomEditorConfigurer bean 吗?控制器建议中的 init binder 就足够了吗?
  • 似乎不适用于RequestBody。有什么办法吗?
【解决方案2】:

我想你必须implement a Custom PropertyEditor

类似这样的:

public class ProductEditor extends PropertyEditorSupport{

    @Override
    public void setAsText(final String text){
        setValue(Product.valueOf(text.toUpperCase()));
    }

}

如何绑定请看GaryF's answer

如果您在枚举常量中使用小写字母(您可能不应该,但仍然如此),这是一个更宽容的版本:

@Override
public void setAsText(final String text){
    Product product = null;
    for(final Product candidate : Product.values()){
        if(candidate.name().equalsIgnoreCase(text)){
            product = candidate;
            break;
        }
    }
    setValue(product);
}

【讨论】:

  • 似乎 GenericConversionService.convert 妨碍了这种类型的解决方案。
  • @dom farr:是的,如果你使用ConversionService,你还需要implement a custom Converter
  • 是的,这是我首先尝试的。它只是没有用,所以我来这里问这个问题。也许我没有正确注册?我使用了 ConversionServiceFactoryBean,但正如我所说,不行。
  • Sean,我正在使用 spring boot,并且在将字符串映射到作为我的控制器参数的对象上的枚举之后,似乎调用了 initbinder 方法。我错过了什么吗?
  • @AlexanderSuraphel 见上面的 cmets。这些天来,ConversionService 可能默认启用。实现自定义转换器
【解决方案3】:

也可以创建一个通用转换器,它可以像这样与所有枚举一起工作:

public class CaseInsensitiveConverter<T extends Enum<T>> extends PropertyEditorSupport {

    private final Class<T> typeParameterClass;

    public CaseInsensitiveConverter(Class<T> typeParameterClass) {
        super();
        this.typeParameterClass = typeParameterClass;
    }

    @Override
    public void setAsText(final String text) throws IllegalArgumentException {
        String upper = text.toUpperCase(); // or something more robust
        T value = T.valueOf(typeParameterClass, upper);
        setValue(value);
    }
}

用法:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(MyEnum.class, new CaseInsensitiveConverter<>(MyEnum.class));
}

或者像 skaffman 解释的那样在全球范围内

【讨论】:

  • 很好,我喜欢泛型类!它可能效率不高,但我更喜欢使用 equalsIgnoreCase 而不是你的 toUpperCase+valueOf。 getEnumConstants 应该在 typeParameterClass 字段之外可用。将此实现与@SeanPatrickFloyd 中的方法相结合
  • 重新考虑在每个请求上迭代枚举常量太痛苦了。我将在 CaseInsensitiveConverter 中添加一个 HashMap 以加快映射速度。
【解决方案4】:

在 Spring Boot 2 中,您可以使用 ApplicationConversionService。它提供了一些有用的转换器,尤其是org.springframework.boot.convert.StringToEnumIgnoringCaseConverterFactory - 负责将字符串值转换为枚举实例。这是我设法找到的最通用的(我们不需要为每个枚举创建单独的转换器/格式化程序)和最简单的解决方案。

import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        ApplicationConversionService.configure(registry);
    }
}

我知道问题是关于 Spring 3 的,但这是在 google 搜索 spring mvc enums case insensitive 短语时的第一个结果。

【讨论】:

    【解决方案5】:

    要添加到@GaryF 的答案并解决您对它的评论,您可以通过将全局自定义属性编辑器注入自定义AnnotationMethodHandlerAdapter 来声明它们。 Spring MVC 通常默认注册其中一个,但如果你选择,你可以给它一个特殊配置的,例如

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
      <property name="webBindingInitializer">
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
          <property name="propertyEditorRegistrars">
            <list>
              <bean class="com.xyz.MyPropertyEditorRegistrar"/>
            </list>
          </property>
        </bean>
      </property>
    </bean>
    

    MyPropertyEditorRegistrarPropertyEditorRegistrar 的一个实例,它反过来用Spring 注册自定义PropertyEditor 对象。

    只需声明这一点就足够了。

    【讨论】:

    • @skaffman。如果我删除所有控制器中的单个 initBinder,似乎无法使其正常工作。不过我会继续挖掘这个。谢谢
    • @dom:这个方法确实有效,我自己也用过,但我可能有些细节有误
    • @skaffman。也许这是我的问题:jira.springsource.org/browse/SPR-7077
    • @dom:是的,很好,这确实是问题所在。您是否特别需要&lt;mvc:annotation-driven/&gt;?除非您使用 Jackson/JSON 映射或 @Valid,否则您可以忽略它。
    • @skaffman。不幸的是,Jackson/JSON 映射和 @Valid 正在被使用,这意味着我现在必须在每个控制器中坚持使用 @InitBinder。
    猜你喜欢
    • 2011-06-04
    • 2014-08-01
    • 2023-01-18
    • 2015-10-27
    • 2021-08-16
    • 2019-07-01
    • 1970-01-01
    • 2013-11-02
    • 1970-01-01
    相关资源
    最近更新 更多