Spring MVC Controller传递枚举值的实例

时间:2021-12-15 19:51:44

Spring MVC Controller传递枚举值

功能描述

本文将通过一个小示例,展示在请求参数中传递枚举值。

枚举定义

角色类定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum RoleEnum {
 EMPLOYEE((short)1, "Employee"), MANAGER((short)2, "Manager");
 
 private Short value;
 private String desc;
 
 private RoleEnum(Short value, String desc) {
  this.value = value;
  this.desc = desc;
 }
 
 public Short value() {
  return value;
 }
 
 public String desc() {
  return desc;
 }
}

定义Controller类

?
1
2
3
4
5
6
7
8
9
@RestController
@Slf4j
public class HomeController {
 @GetMapping("/home")
 public String getDemo(@RequestParam("role") RoleEnum role) {
  log.info("Role Enum:{}" + role); 
  return role.desc();
 }
}

说明: 在这里RoleEnum之内作为@RequestParam参数。

请求示例

Case1: http://localhost:8080/home?role=EMPLOYEE

结论: 正确返回, Employee

Case2: http://localhost:8080/home?role=Employee

报错,具体信息如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu May 14 11:21:32 CST 2020
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'org.course.data.domain.RoleEnum'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam org.course.data.domain.RoleEnum] for value 'employee'; nested exception is java.lang.IllegalArgumentException: No enum constant org.course.data.domain.RoleEnum.employee
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.course.data.domain.RoleEnum'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam org.course.data.domain.RoleEnum] for value 'employee'; nested exception is java.lang.IllegalArgumentException: No enum constant org.course.data.domain.RoleEnum.employee
   at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:133)
   at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:127)
   at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)
   at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
   at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
   at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)
   at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:798)
   at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
   at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
   at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
   at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
   at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
   at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94)
   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
   at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
   at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
   at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
   at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1639)
   at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
   at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
   at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam org.course.data.domain.RoleEnum] for value 'employee'; nested exception is java.lang.IllegalArgumentException: No enum constant org.course.data.domain.RoleEnum.employee
   at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
   at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
   at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:129)
   at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
   at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:53)
   at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:693)
   at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:125)
   ... 51 more
Caused by: java.lang.IllegalArgumentException: No enum constant org.course.data.domain.RoleEnum.employee
   at java.lang.Enum.valueOf(Enum.java:238)
   at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:52)
   at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:38)
   at org.springframework.core.convert.support.GenericConversionService$ConverterFactoryAdapter.convert(GenericConversionService.java:436)
   at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
   ... 57 more

实现分析:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
    StringToEnumConverterFactory() {
    }
    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverterFactory.StringToEnum(ConversionUtils.getEnumType(targetType));
    }
    private class StringToEnum<T extends Enum> implements Converter<String, T> {
        private final Class<T> enumType;
        public StringToEnum(Class<T> enumType) {
            this.enumType = enumType;
        }
        public T convert(String source) {
            return source.isEmpty() ? null : Enum.valueOf(this.enumType, source.trim());
        }
    }
}

底层实现为将String转换为Enum值的过程。

结论

基于枚举可以限定具体值,简单易用。但是缺点是在扩展enum之时,无法做法平滑过度升级,会吹安短暂的异常情况。

Spring MVC 枚举传值问题

今天遇到算是棘手的一个枚举的问题,后台Controller参数是一个对象,而对象里面有个枚举类型的属性,死活不能传值。

最后找到解决方案

实体对象:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Entity
@Table(name="xx_sn")
@SequenceGenerator(name = "sequenceGenerator", sequenceName = "xx_sn_sequence")
public class ShoppTest extends BaseEntity{
    private static final long serialVersionUID = 2756395514949325790L;
    /**
     * 枚举
     * @author Administrator
     *
     */
    public enum Type{
        /** 商品 */
        product,
 
        /** 订单 */
        order,
 
        /** 收款单 */
        payment,
 
        /** 退款单 */
        refunds,
 
        /** 发货单 */
        shipping,
 
        /** 退货单 */
        returns
    }
    @Column(nullable = false, updatable = false, unique = true)
    private Type type;
    
    @Column(nullable = false)
    private String lastValue;
    
    public String getLastValue() {
        return lastValue;
    }
    public void setLastValue(String lastValue) {
        this.lastValue = lastValue;
    }
    
    public Type getType() {
        return type;
    }
    
    public void setType(Type type) {
        this.type = type;
    }

Controller代码:

?
1
2
3
4
5
6
@RequestMapping("/save")
public String save(ShoppTest st,String tp){
 st.setType(Enum.valueOf(Type.class, tp));
 shoppTestServiceImpl.save(st);
 return "redirect:list.jhtml";
}

前台代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<form id="inputForm" action="save.jhtml" method="post">
<table class="input">
<tr>
<th>
${message("test.lastValue")}:
</th>
<td colspan="2">
<input type="text" name="lastValue" class="text">
</td>
</tr>
<tr>
<th>
<span class="requiredField">*</span>${message("test.type")}:
</th>
<td colspan="2">
<input type="text" name="tp" class="text" maxlength="200" />
</td>
</tr>
</table>
<table class="input">
<tr>
<th>
</th>
<td>
<input type="submit" class="button" value="${message("admin.common.submit")}" />
<input type="button" class="button" value="${message("admin.common.back")}" οnclick="location.href='list.jhtml'" />
</td>
</tr>
</table>
</form>

因为springMVC对于枚举这种特殊的数据类型不能直接进行数据注入,所以访问Controller的时候直接就是400错误。

所以我这里用String去接收前台传来的枚举需要的数据,也就是“tp”,用到Enum.ValueOf();

ValueOf需要两个参数,第一个参数类型就是自己定义的枚举类类型,第二个参数是你枚举里面

定义的字段名字,这个字段就对应了你枚举所得到的值。

最终取到值后赋值给对象里面的枚举。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blade.blog.csdn.net/article/details/106116025