使用表单主体的 Spring MVC 控制器方法映射

2024-04-27

我正在构建一个小型应用程序,作为工作中某些第三方库的客户端。 API 指出Webhook需要响应一些异步事件,但除了更改之外,它们的所有方法都具有完全相同的签名_method调用之间的字段。例如,我有一个_method = ping, media, etc.

我希望控制器上有单独的方法来响应其中每一种方法。如果应用程序允许我为每个方法指定不同的 URL,那么使用 Spring MVC 就会很容易@RequestMapping对于他们每个人来说。但我必须指定一个端点来接收所有呼叫。

有没有办法(例如使用Spring的HttpMessageConverter或类似的东西)根据请求正文映射不同的控制器方法?我已经尝试过@RequestBody, @RequestParam但似乎没有发现任何东西。

我真的,really不想用一堆case, switch前端控制器上的方法根据我的调度动作_method我的 POST 数据附带的字段,所以我碰巧相信以前有人遇到过这个问题并聪明地解决了它。

多谢!

编辑1:提供源代码

@Controller
@RequestMapping("/webhooks")
public class WebhookController {

    @RequestMapping(method = RequestMethod.POST, params = {"_method=ping"})
    @ResponseBody
    public String ping(){
        return "pong";
    }

    @RequestMapping(method = RequestMethod.POST, params = {"_method=media"})
    @ResponseBody
    public String media(){
        return "media";
    }
}

这是答案:

{
  "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"
}

好的,我成功了。答案有点棘手,所以我想在这里注册,以防有人遇到这样的问题。

@Neil McGuigan 在他的评论中为我指出了正确的方向,但我一开始没有注意。这里的罪魁祸首是一个非常非常very我们的远程应用程序端的 API 设计很糟糕。

_method是一个用于指定非标准 HTTP 动词的字段,例如PUT, PATCH, DELETE, TRACE等等。该字段的过滤条件为HiddenHttpMethodFilterHttpServletRequest是用这个“新”方法包装的。你可以看到文件的来源 https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/filter/HiddenHttpMethodFilter.java#L52怎么运行的。

正如我想要的_method字段来通过过滤器而不修改整个请求(并导致错误,因为没有这样的动词pingor message在`RequestMethod)上,我首先必须停用过滤器。这可以通过两种方式完成:

  1. 我可以阻止 Spring Boot 自动配置 Spring MVC,跳过WebMvcAutoConfiguration从被加载时ApplicationContext已加载。你可以想象这是一个很大、很大、很大的NO因为,嗯,things http://giphy.com/gifs/HhTXt43pk1I1W/fullscreen可能会发生。

  2. 我可以用一个FilterRegistrationBean禁用坏过滤器。非常简单明了,这是我选择使用的方法:

    @Bean
    public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    }
    

最后但并非最不重要的一点是,我决定给予HiddenHttpMethodFilter以某种方式进行一点扩展improve请求是如何通过的。 Java EE 规范在 Servlet 规范命令中非常明确,它指出:

Thou should不改变你方的要求。您必须尊重发件人(类似的事情)

虽然我同意这一点,但为了我的心理稳定,我还是决定改变它。为了实现这一点,我们可以使用一个简单的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);
        }
    }
}

所以,它所做的就是用一个新的包装请求x-whatools-method当标题位于我的标题中时whatoolsMethods列表。有了这个,我可以轻松使用@RequestMapping's headers属性并将请求映射到正确的控制器方法。

回到最初的问题,我几乎可以肯定(嗯,99.95% 应该完全确定,但我们不要冒险)params属性于@RequestMapping仅适用于 GET URI 上的请求参数,例如http://foo.bar/?baz=42。它无法过滤请求正文中发送的参数。

感谢尼尔的指导,即使很小!我希望这可以帮助别人。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用表单主体的 Spring MVC 控制器方法映射 的相关文章

随机推荐

  • 解析嵌套的 JSON Swift 3 [重复]

    这个问题在这里已经有答案了 我正在尝试在 Swift 3 中解析复杂的 JSON 文件 我在获取值时遇到问题 我找到了一些示例代码并尝试对其进行了一些调整 但它不起作用我当前收到错误 Type NSArray 没有下标成员 我什至不确定这是
  • WebStorm/PHPStorm 中的智能选项卡有什么作用?

    我对智能选项卡在一般偏好中的作用感到困惑 它说 仅通过空格即可对必要的列进行精细对齐 如果 如果未选中此复选框 则使用空格进行对齐 必要的 这是否意味着在这两种情况下都使用空格来对齐 这是否意味着它总是在后者中使用空格 而仅有时在前者中使用
  • 如何根据功能(自动)通过属性查看或隐藏字段?

    我只想查看员工 他的经理和 hr group hr user 组的 working hours 字段 如何隐藏该字段自动执行 无需编辑表单或触发按钮 class InheritHrEmployee models Model inherit
  • Grails“max”带有关联的子查询,仅获取最新的 hasMany

    简化的域模型 Txn 如交易中 有许多 TxnStatus TxnStatus 有一个日期时间 这是一个遗留映射 所以我无法更改 DB Txn 上的映射 static mapping txnStatus column MessageID i
  • JavaScript 运行时错误:无法获取未定义或空引用的属性“msie”

    我只是尝试更改我的 jquery ui 对母版页的引用 我仅在 Internet Explorer 上收到上述错误 我在 Firefox 和 Chrome 上没有收到错误消息 这是抛出错误的 jquery 代码 return a brows
  • 仅横向应用程序中的 GameCenter 身份验证抛出 UIApplicationInvalidInterfaceOrientation

    问题 如果用户未登录 GameCenter 帐户 GameCenter 身份验证视图以纵向模式启动 在 ios 5 中 有一个模式对话框 要求登录 但是如果我在 xcode 项目摘要 或supportedInterfaceOrientati
  • 根据使用频率随机生成字母?

    如何根据常用语音中的使用频率随机生成字母 任何伪代码都值得赞赏 但如果用 Java 实现就更棒了 否则 只需朝正确的方向戳一下就会有所帮助 注意 我不需要生成使用频率 我确信我可以很容易地查找到它 我假设您将频率存储为 0 到 1 之间的浮
  • 使用IntelliJ编译Java 7,配置麻烦

    我正在使用 IntelliJ 并尝试让它与 Java 7 一起工作 我的 JDK 设置为指向最新的 JDK 此外 项目也设置使用它 然而 当尝试编写 Java7 代码时 它的语法无法被识别 请问我缺少什么 Set the 项目语言水平 to
  • 如何计算Java数组的内存大小?

    我知道如何通过添加三个部分来计算Java对象的内存大小 标头 属性 引用 我还知道Java数组也是一个对象 但是当我读到 Understanding the JVM Advanced Features and Best Practices
  • 如何检查 C# 中动态匿名类型上是否存在属性?

    我有一个匿名类型对象 我从方法中以动态方式接收该对象 我想检查该对象上是否存在属性 var settings new Filename temp txt Size 10 function void Settings dynamic sett
  • ANSI C - 清除字符串

    我有一个这样声明的字符串 str malloc sizeof char 128 我想彻底清除它 这样当我这样做时strncat 操作时 新字符将被写入到开头str 我需要清除它的原因是我正在用它本身的简化版本重写它 删除多余的空格 我建议你
  • 如何编写通用 C 函数来调用 Win32 函数?

    为了允许从脚本语言 用 C 编写 访问 Win32 API 我想编写一个如下所示的函数 void Call LPCSTR DllName LPCSTR FunctionName LPSTR ReturnValue USHORT Argume
  • optim() 中的错误:搜索单变量函数的全局最小值

    我正在尝试优化 R 中的函数 该函数是仅估计时负二项式的似然函数mu范围 这应该不是问题 因为该函数显然只有一个最大值点 但是 我无法达到理想的结果 需要优化的函数为 EMV lt function data par Mi lt par P
  • 如何使用 Autoit / Autohotkey 模仿 Visual Studio 的“Ctrl-K、C”两步宏行为?

    I m trying to set up AutoHotkey http www autohotkey com macros for some common tasks and I want the hotkeys to mimic Vis
  • Angular 4 中的多个顺序 API 调用

    我有一系列图像对象 console info gallery galleryArray 该数组的长度可以不同 我必须对该数组的每个项目发出 POST 请求 只有在前一个请求完成后 才能执行下一个请求 所以我尝试发出一系列可观察的请求 如下所
  • JSF 2.0 动态属性,无需创建新组件

    如何向未定义这些属性的组件添加新属性而不创建自己的属性 我想做这样的事情
  • 开始作业时无法识别功能

    我在模块 sysinfo psm1 中创建了一个函数 Get Uptime 并导入了该模块 C pstools gt get command Module sysinfo CommandType Name Definition Functi
  • 按任意顺序对 SQL 行输出进行排序?

    因此 在我的数据库中 我存储乐器名称 以及各种其他属性 比方说id是主键 并且name是唯一的密钥 在 PHP 脚本中 我按仪器类别选择项目 如下所示 name mysql real escape string POST name row
  • Django 1.7 makemigrations 将表重命名为 None

    我必须将一些模型从一个应用程序移动到另一个应用程序 并且我按照此答案中的说明进行操作https stackoverflow com a 26472482 188614 https stackoverflow com a 26472482 1
  • 使用表单主体的 Spring MVC 控制器方法映射

    我正在构建一个小型应用程序 作为工作中某些第三方库的客户端 API 指出Webhook需要响应一些异步事件 但除了更改之外 它们的所有方法都具有完全相同的签名 method调用之间的字段 例如 我有一个 method ping media