Spring 3.0 MVC 绑定枚举大小写敏感
Posted
技术标签:
【中文标题】Spring 3.0 MVC 绑定枚举大小写敏感【英文标题】:Spring 3.0 MVC binding Enums Case Sensitive 【发布时间】:2011-06-04 18:20:13 【问题描述】:如果我在 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());
【问题讨论】:
Spring Boot 中有RelaxedConversionService
和 StringToEnumIgnoringCaseConverterFactory
类,但它们不公开。
这里有一个解决方案使用StringToEnumIgnoringCaseConverterFactory
***.com/questions/55169848/…
我没有对此进行测试,但看起来这会起作用:vianneyfaivre.com/tech/…
【参考方案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 的,但这是在谷歌搜索 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>
MyPropertyEditorRegistrar
是PropertyEditorRegistrar
的一个实例,它反过来用Spring 注册自定义PropertyEditor
对象。
只需声明这一点就足够了。
【讨论】:
@skaffman。如果我删除所有控制器中的单个 initBinder,似乎无法使其正常工作。不过我会继续挖掘这个。谢谢 @dom:这个方法确实有效,我自己使用它,但我可能有一些细节错误 @skaffman。也许这是我的问题:jira.springsource.org/browse/SPR-7077 @dom:是的,很好,这确实是问题所在。您是否特别需要<mvc:annotation-driven/>
?除非您使用 Jackson/JSON 映射或 @Valid
,否则您可以忽略它。
@skaffman。不幸的是,Jackson/JSON 映射和 @Valid 正在被使用,这意味着我现在必须在每个控制器中坚持使用 @InitBinder。以上是关于Spring 3.0 MVC 绑定枚举大小写敏感的主要内容,如果未能解决你的问题,请参考以下文章
使用 Angular JS 在下拉列表中绑定 MVC 枚举列表