如何在Spring-mvc中为请求参数设置别名?

5

在Spring中使用bean对象处理请求参数时,是否有一种方法可以为bean属性定义别名?

@RestController
public class MyServlet {
   @GetMapping
   public void test(MyReq req) {
   }
}

public class MyReq {
   @RequestParam("different-name") //this is invalid
   private String name;
   private int age;
}

当然,@RequestParam不可用,但是否有类似的注释可供使用?

你看过这篇文章了吗?https://dzone.com/articles/customizing-parameter-names - Vasiliy Vlasov
不是开箱即用的。 - M. Deinum
4个回答

2

使用以下方法可以使用注释设置自定义名称:

请参见Bozhos的答案: 如何在绑定Spring MVC命令对象时自定义参数名称

由于我正在使用Spring 4,因此可以添加自定义解析器。

@Configuration
public class AdapterConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        argumentResolvers.add(new AnnotationServletModelAttributeResolver(false));
    }
}

然后可以将其用于获取查询 bean,如下所示:

@SupportsCustomizedBinding
public class MyReq {
   @CommandParameter("different-name") //this is valid now!
   private String name;
}

此外,我也希望匹配get查询参数时不区分大小写,因此我使用了以下类:https://github.com/mdeinum/spring-utils/blob/master/src/main/java/biz/deinum/web/filter/CaseInsensitiveRequestFilter.java。可以按照以下方式进行连接:
@Bean
public CaseInsensitiveRequestFilter caseInsensitiveFilter() {
    return new CaseInsensitiveRequestFilter();
}

2

请求参数通过setter绑定。您可以添加一个额外的带有原始参数名称的setter。类似于:

public class MyReq {
   private String name;
   private int age;

   public void setDifferentName(String differentName) {
      this.name=differentName;
   }
}

注意:此方法仅适用于参数为驼峰式命名的情况,例如differentName=abc。无法处理different-name=abc这样的参数。


1
您可以使用setter来实现。以您的示例为例:
@SpringBootApplication
public class So44390404Application {

    public static void main(String[] args) {
        SpringApplication.run(So44390404Application.class, args);
    }

    @RestController
    public static class MyServlet {
        @GetMapping
        public String test(MyReq req) {
            return req.toString();
        }
    }

    public static class MyReq {
        private String name;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void setDifferent_Name(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "{" + name + age + '}';
        }
    }
}

调用者可能会使用:

$so44390404 curl -XGET 'http://localhost:8000?name=adam&age=42'          
{adam42}%
$so44390404 curl -XGET 'http://localhost:8000?Different_Name=John&age=23'
{John23}% 

更新

如果你正在处理带连字符参数的问题,事情可能会变得有些棘手。

基本上你可以:

  1. 创建一个过滤器来规范化带连字符的参数名称,以便Spring能够成功绑定它们。
  2. 在控制器中将所有请求参数作为原始映射接收,规范化键,然后自己使用所有类型转换功能填充对象。

使用过滤器的选项可能如下所示:

@Component
public static class CustomRequestParametersFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(new RequestParameterNormalizerWrapper(request), response);
    }

    public static class RequestParameterNormalizerWrapper extends HttpServletRequestWrapper {
        public static final String HYPHEN = "-";
        private final Map<String, String[]> parameterMap = new HashMap<>();

        public RequestParameterNormalizerWrapper(HttpServletRequest request) {
            super(request);

            for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
                if (entry.getKey().contains(HYPHEN)) {
                    parameterMap.put(normalize(entry.getKey()), entry.getValue());
                }
                else {
                    parameterMap.put(entry.getKey(), entry.getValue());
                }
            }
        }

        private String normalize(final String key) {
            if (key.contains(HYPHEN)) {
                return WordUtils.capitalizeFully(key, HYPHEN.charAt(0)).replaceAll(HYPHEN, "");
            }
            return key;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return Collections.unmodifiableMap(this.parameterMap);
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return Collections.enumeration(this.parameterMap.keySet());
        }

        @Override
        public String getParameter(String name) {
            return super.getParameter(normalize(name));
        }

        @Override
        public String[] getParameterValues(String name) {
            return parameterMap.get(normalize(name));
        }
    }
}

通过上一个例子,应该可以直接使用。

第二个选项可能是:

@RestController
public static class MyServlet {

    @GetMapping
    public String test(@RequestParam Map<String, String> pvs) {
        final MyReq req = new MyReq();
        final BeanWrapper beanWrapper = new HyphenAwareBeanWrapper(req);
        beanWrapper.setPropertyValues(pvs);
        return req.toString();
    }
}

并且包装器:

public static class HyphenAwareBeanWrapper extends BeanWrapperImpl {
    public static final String HYPHEN = "-";

    public HyphenAwareBeanWrapper(Object object) {
        super(object);
    }

    @Override
    public void setPropertyValues(Map<?, ?> map) throws BeansException {
        final ArrayList<PropertyValue> propertyValueList = new ArrayList<>(map.size());
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            final String key = entry.getKey().toString().contains(HYPHEN)
                    ? WordUtils.capitalizeFully(entry.getKey().toString(), HYPHEN.charAt(0)).replaceAll(HYPHEN, "")
                    : entry.getKey().toString();
            propertyValueList.add(new PropertyValue(key, entry.getValue()));
        }
        super.setPropertyValues(new MutablePropertyValues(propertyValueList));
    }
}

测试:

$ curl -XGET 'http://localhost:8000?name=John&age=42'
{John42}%
$ curl -XGET 'http://localhost:8000?different-name=John&age=42'
{John42}%

如果我重命名setter,我也可以完全重命名属性+getter+setter。但这不是我想要的。想象一下,输入参数应该作为“MY-INPUT”发送。像my-input=test这样的setter是无效的变量名称。尽管我想提供这样的参数功能。 - membersound
是的,您也可以重命名字段和getter,但这并非必须。您可以为一个属性拥有任意多个setter。至于连字符分隔的参数名称——我已经更新了一个答案。 - Bohdan Levchenko

1
与Sergiy Dakhniy/Bohdan Levchenko评论相似。请求参数通过setter绑定。您可以添加一个额外的setter,其参数名来自于您的传入请求。例如:
@GetMapping(value = "/do-something")
public ResponseEntity<String> handleDoSomething(@Valid MyReq myReq) {
...
}

public class MyReq {
  private String name;

  public void setDifferent_name(String name) {
    this.name = name;
  }
}

例如:http://www.example.com/do-something?different_name=Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接