使用表单体映射Spring MVC控制器方法
我正在构建一个小应用程序,作为某个第三方库的客户端。API声明需要一个使用表单体映射Spring MVC控制器方法,spring,spring-mvc,jackson,spring-boot,Spring,Spring Mvc,Jackson,Spring Boot,我正在构建一个小应用程序,作为某个第三方库的客户端。API声明需要一个Webhook来响应一些异步事件,但是它们的所有方法都具有非常相同的签名,除了调用之间不断变化的\u method字段。例如,我有一个\u方法=ping,媒体,等等 我希望在我的控制器上有单独的方法来响应这些方法中的每一种。如果应用程序允许我为每种方法指定不同的URL,那么对每种方法使用SpringMVC的@RequestMapping就很容易了。但我必须指定一个端点来接收所有呼叫 是否有一种方法(例如使用Spring的Htt
Webhook
来响应一些异步事件,但是它们的所有方法都具有非常相同的签名,除了调用之间不断变化的\u method
字段。例如,我有一个\u方法
=ping
,媒体
,等等
我希望在我的控制器上有单独的方法来响应这些方法中的每一种。如果应用程序允许我为每种方法指定不同的URL,那么对每种方法使用SpringMVC的@RequestMapping
就很容易了。但我必须指定一个端点来接收所有呼叫
是否有一种方法(例如使用Spring的HttpMessageConverter
或类似的方法)根据请求主体映射不同的控制器方法?我已经尝试了@RequestBody
,@RequestParam
,但似乎没有找到任何东西
我真的,真的不想使用一堆前控制器上的案例,切换方法,根据我的POST数据附带的\u方法字段来调度操作,所以我碰巧相信有人以前有过这个问题,并聪明地解决了它
非常感谢
编辑1:提供源代码
答案是:
{
"timestamp": 1440875190389,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.UnsatisfiedServletRequestParameterException",
"message": "Parameter conditions \"_method=ping\" not met for actual request parameters: ",
"path": "/webhooks"
}
您可以在请求映射中使用参数:
@RequestMapping(value=“/foo”,params={“\u method=ping”})
假设这些是post参数
params
确实为POST工作,我向你保证
这是我的控制器:
@Controller
@RequestMapping("/test1")
public class ParamTestController {
@RequestMapping(method = RequestMethod.POST)
@ResponseBody String getA(){
return "A";
}
@RequestMapping(method = RequestMethod.POST, params = {"b"})
@ResponseBody String getB(){
return "B";
}
}
这是我的测试:
好的,我让它工作了。答案有点棘手,所以如果有人有这样的问题,我想在这里注册
@Neil McGuigan在他的评论中为我指出了正确的方向,但一开始我没有注意。这里的罪魁祸首是远程应用程序方面非常非常糟糕的API设计
\u method
是一个用于指定非标准HTTP谓词的字段,如PUT
、PATCH
、DELETE
、TRACE
等。此字段由HiddenHttpMethodFilter
过滤,而HttpServletRequest
用此“新”方法包装。你可以看看它是如何工作的
由于我想让这个\u方法字段在不修改整个请求的情况下通过过滤器(并导致错误,因为在'RequestMethod'上没有ping
或message
这样的动词),我首先必须停用过滤器。这可以通过两种方式实现:
我可以停止Spring Boot自动配置Spring MVC,在加载ApplicationContext
时跳过WebMvcAutoConfiguration
。你可以想象,这是一个非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常
我可以使用FilterRegistrationBean
来禁用坏的过滤器。非常简单和直接,这是我选择使用的方法:
@Bean
public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
最后但并非最不重要的一点是,我决定对hiddenhttmpmethodfilter
进行一点扩展,以某种方式改进请求的通过方式。Java EE规范在Servlet规范命令中非常明确,其中规定:
你不应该改变你这边的要求。你必须尊重发信人(诸如此类)
虽然我同意这一点,但为了我的精神稳定,我还是决定改变它。为了实现这一点,我们可以使用一个简单的HttpServletRequestWrapper
,覆盖所选的方法,并使用包装的部分过滤原始请求。我最后做了这样的事情:
public class WhatoolsHiddenHttpMethodFilter extends OrderedHiddenHttpMethodFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(OrderedHiddenHttpMethodFilter.DEFAULT_METHOD_PARAM);
if("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
List<String> whatoolsMethods = Arrays.asList("ping", "message", "carbon", "media", "media_carbon", "ack");
if(whatoolsMethods.contains(paramValue)){
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, "POST", paramValue);
filterChain.doFilter(wrapper, response);
} else {
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, method, null);
filterChain.doFilter(wrapper, response);
}
} else {
filterChain.doFilter(request, response);
}
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
private final String whatoolsMethod;
public HttpMethodRequestWrapper(HttpServletRequest request, String method, String whatoolsMethod) {
super(request);
this.method = method;
this.whatoolsMethod = whatoolsMethod;
}
@Override
public String getMethod() {
return this.method;
}
@Override
public String getHeader(String name) {
if("x-whatools-method".equals(name)){
return this.whatoolsMethod;
}
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
if(this.whatoolsMethod != null){
names.add("x-whatools-method");
}
return Collections.enumeration(names);
}
}
}
公共类WhatoolsHiddenHttpMethodFilter扩展了OrderedHiddenHttpMethodFilter{
@凌驾
受保护的void doFilterInternal(HttpServletRequest请求、HttpServletResponse响应、FilterChain FilterChain)抛出ServletException、IOException{
String paramValue=request.getParameter(OrderedHiddenHttpMethodFilter.DEFAULT_METHOD_PARAM);
if(“POST.equals(request.getMethod())&&StringUtils.hasLength(paramValue)){
String方法=paramValue.toUpperCase(Locale.ENGLISH);
列出whatoolsMethods=Arrays.asList(“ping”、“message”、“carbon”、“media”、“media\u carbon”、“ack”);
if(whatoolsMethods.contains(paramValue)){
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper包装器=新WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(请求,“POST”,参数值);
filterChain.doFilter(包装器,响应);
}否则{
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper包装器=新WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(请求,方法,null);
filterChain.doFilter(包装器,响应);
}
}否则{
filterChain.doFilter(请求、响应);
}
}
私有静态类HttpMethodRequestWrapper扩展了HttpServletRequestWrapper{
私有最终字符串方法;
私人最终字符串whattoolsmethod;
公共HttpMethodRequestWrapper(HttpServletRequest请求、字符串方法、字符串WhatToolsMethod){
超级(请求);
这个方法=方法;
this.whatoolsMethod=whatoolsMethod;
}
@凌驾
公共字符串getMethod(){
返回此.method;
}
@凌驾
公共字符串getHeader(字符串名称){
if(“x-whatools-method”.equals(name)){
返回此。whatoolsMethod;
}
返回super.getHeader(name);
}
@凌驾
公共枚举getHeaderNames(){
列表名称=Collections.List(super.getHeaderNames());
if(this.whatoolsMethod!=null){
名称。添加(“
public class WhatoolsHiddenHttpMethodFilter extends OrderedHiddenHttpMethodFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(OrderedHiddenHttpMethodFilter.DEFAULT_METHOD_PARAM);
if("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
List<String> whatoolsMethods = Arrays.asList("ping", "message", "carbon", "media", "media_carbon", "ack");
if(whatoolsMethods.contains(paramValue)){
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, "POST", paramValue);
filterChain.doFilter(wrapper, response);
} else {
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, method, null);
filterChain.doFilter(wrapper, response);
}
} else {
filterChain.doFilter(request, response);
}
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
private final String whatoolsMethod;
public HttpMethodRequestWrapper(HttpServletRequest request, String method, String whatoolsMethod) {
super(request);
this.method = method;
this.whatoolsMethod = whatoolsMethod;
}
@Override
public String getMethod() {
return this.method;
}
@Override
public String getHeader(String name) {
if("x-whatools-method".equals(name)){
return this.whatoolsMethod;
}
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
if(this.whatoolsMethod != null){
names.add("x-whatools-method");
}
return Collections.enumeration(names);
}
}
}