【问题标题】:Spring Boot controller not responding to POST requestSpring Boot 控制器没有响应 POST 请求
【发布时间】:2019-10-17 06:36:07
【问题描述】:

我正在使用具有云功能的收据打印机。它与我正在实施的服务器规范进行通信。它每隔 x 秒轮询一个带有 POST 请求的 URL,当 POST 响应包含某条信息时,打印机会向该 URL 发送一个 GET 请求以获取要打印的信息。

我正在将打印服务器实现为 Spring Boot 服务器,我在使用 POST 方法时遇到了一些奇怪的问题,我需要一些帮助。

我的问题是从打印机到服务器的 POST 请求永远不会到达控制器。但是,我可以从 Postman 向完全相同的 URL 发送 POST 请求,并由控制器处理。

网址很简单:https://www.[my-domain].com:[port-number]/cloudprint

另外,我尝试将控制器方法复制到另一个 Spring(不是 Boot)应用程序,在 Apache 后面的 Tomcat 实例上运行,并且在那里,来自打印机的 POST 请求由控制器方法处理。我可以在 Apache 日志和 Tomcat 日志中看到它们。目前轮询频率为 10 秒。

这是控制器的外观:

package com.[my-domain].[application-name].controller;

[a bunch of imports]

@RestController
@CrossOrigin
public class PrintController {
    Logger logger = LoggerFactory.getLogger(PrintController.class);

    @RequestMapping(value="/cloudprint", method=RequestMethod.POST,
            headers={"Accept=application/json"})
    @ResponseStatus(HttpStatus.CREATED)
    public @ResponseBody String printPost() { 
        logger.debug("in printPost");
        return "OK";
    }

    @RequestMapping(value="/cloudprint", method=RequestMethod.GET,
            headers={"Accept=application/json"})
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody String printGet(HttpServletRequest request) {
        logger.debug("in printGet");
        return "OK";
    }

    @RequestMapping(value="/cloudprint", method=RequestMethod.DELETE,
            headers={"Accept=application/json"})
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody String printDelete() {
        logger.debug("in printDelete");
        return "OK";
    }
}

这可能是什么原因造成的?我可以测试什么来解决这个问题?

---在 2019-06-03 @13:21 cet 下方添加信息--- 由于我有一个接受来自打印机的 POST 请求的常规 Spring(非引导)应用程序,因此我能够在传入请求中记录信息。所以我就这么做了。

这是来自打印机的 POST 请求之一,Spring 引导控制器不接受该请求:

auth type: null
content type: application/json
-- HEADERS --
Header: host : dev.[our-domain-name].com
Header: accept : */*
Header: user-agent : Cente HTTPc
Header: content-type : application/json
Header: content-length : 303
Header: connection : keep-alive
QueryString: null
-- PARAMETERS --
END

这是从 Postman 到完全相同的 URL 的 POST 请求之一,它被 Spring boot 控制程序接受:

auth type: null
cotent type: application/json
-- HEADERS --
Header: content-type : application/json
Header: cache-control : no-cache
Header: Postman-Token : caf99fa1-4730-4193-aab3-c4874273661d
Header: user-agent : PostmanRuntime/7.6.0
Header: accept : */*
Header: host : dev.[our-domain-name].com
Header: accept-encoding : gzip, deflate
Header: content-length : 0
Header: connection : keep-alive
QueryString: null
-- PARAMETERS --
END

分析: 1. 用户代理头不同。 2. content-length header 不同。 3. Postman 请求具有来自云打印机的请求所没有的三个标头。它们是:cache-control、Postman-token 和accept-encoding

---在 2019-06-03 @17:56 cet 下方添加信息--- 好的,我想出了如何记录消息正文。它是一个格式良好的 json 结构,确实是 303 个字符:

{"status": "29 a 0 0 0 0 0 0 0 0 0 0",
 "printerMAC": "00:11:62:1b:xx:xx",
 "statusCode": "200%20OK",
 "printingInProgress": false,
 "clientAction": null,
 "display": [
   {"name": "MainDisplay" ,
    "status": {"connected": false}}],
 "barcodeReader": [
   {"name": "MainBCR" ,
    "status": {"connected": false,"claimed": false}}]}

我创建了相应的类,并将Boot应用程序中的POST方法改成这样:

@RequestMapping(value="/cloudprint", method=RequestMethod.POST,
        headers={"Accept=application/json"})
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody String printPost(@RequestBody PrinterPostRequest printerPostRequest,
        HttpServletRequest request) {  
    HttpRequestLogger.log(request);
    return "OK";
}

仍然没有从打印机接收 POST 请求。它接收 Postman 请求并成功将请求正文 json 编组到类中。

---在 2019-06-05 @10:16 cet 下方添加信息--- 我有个主意。使用 Spring RestTemplate,我向 Boot 应用程序发送了一个 POST 请求,其标题和有效负载与打印机发送的请求相同。我收到一条带有此消息的 org.springframework.web.client.ResourceAccessException:

I/O error on POST request for "https://boot.[my-domain].com:8443/cloudprint":sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

【问题讨论】:

  • 首先在本地运行你的应用,如果端口是8080,然后尝试使用localhost:8080/cloudprint进行post调用并检查
  • 在 DEBUG 模式下,日志通常会打印出侦听器正在处理的 URL 端点。您可能想从那里开始。
  • 检查打印机的代理/防火墙配置。
  • @sambit: 我将无法让打印机调用 localhost
  • @MatsAndersson 你有一个有效的 SSL 证书还是自签名的? Spring Boot 会终止 SSL 还是有前端代理?

标签: java spring rest spring-boot security


【解决方案1】:

如果您使用 Spring Boot 2,则可能由于 csrf 保护而发生。它只影响非 GET 请求,并且在 Spring Boot 2 上默认打开 on,但在早期版本中它是关闭的。

所以它很好地反映了您的问题描述 - 虽然我不完全理解它如何通过 Postman 工作......但是它有可能以某种方式自动处理它......

无论如何都值得一试:

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}

【讨论】:

  • 我添加了一个您建议的配置类,但不幸的是它没有任何区别。我仍然认为这值得深入挖掘。明天我回来工作时我会这样做。感谢您的意见。
  • 您的 Spring Boot 应用程序中一定有错误,因为您可以发送以访问来自 Postman 的 POST 请求。因此,请向我们展示您在 Spring Boot 应用程序中发送 POST 请求的代码。
  • @meziane:我没有在我的 Spring Boot 应用程序中发送 POST 请求。我收到一个。正如我在原始帖子中明确指出的那样,该请求是从收据打印机发送的。这是打印机:star-emea.com/products/cloudprnt
  • @mats-andersson 对不起,我误会了你:我需要一杯咖啡(:
  • 您是否在 POST 请求的正文中发送任何内容?如您所见,here 不允许不发送正文,因为@RequestBodyrequired 属性的默认值为true
【解决方案2】:

您好,您正在通过 HTTPS 访问 REST 服务。您从服务器收到的消息非常清楚。

sun.security.validator.ValidatorException:PKIX 路径构建失败: sun.security.provider.certpath.SunCertPathBuilderException:无法 找到请求目标的有效认证路径;嵌套异常是 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:PKIX 路径构建失败: sun.security.provider.certpath.SunCertPathBuilderException:无法 找到请求目标的有效认证路径

您尚未将证书添加到您的 ca 存储中。此处提供了有关如何执行此操作的详细分步指南

Getting ssl.SSLHandshakeException when using REST client with header but works fine with PostMan

【讨论】:

  • 是的,这就是我所怀疑的。 Let's encrypt 证书真的不被认为可以吗?这就是我所拥有的。
【解决方案3】:

每当 Java 尝试通过 SSL 连接到另一个应用程序(例如:HTTPS、IMAPS、LDAPS)时,它只有在它可以信任的情况下才能连接到该应用程序。

在 Java 世界中处理信任的方式是您有一个密钥库(通常是 $JAVA_HOME/lib/security/cacerts),也称为信任库。这包含所有已知证书颁发机构 (CA) 证书的列表,Java 将仅信任由其中一个 CA 签名的证书或该密钥库中存在的公共证书。

因此,此问题是由自签名证书(CA 未对其进行签名)或 Java 信任库中不存在的证书链引起的。它不信任证书,无法连接到应用程序。

【讨论】:

    【解决方案4】:

    我不确定您发出帖子请求时的格式。您可以使用邮递员来验证您的应用程序可以处理哪种类型的帖子。并发送相同类型的 post 请求,我相信您可以轻松找到它们的示例代码。通常,当我们从 html 页面提交帖子时,我们使用 x-www-form-urlencoded。

    【讨论】:

    • 它来自云打印机,我无法访问它的代码,只有他们的文档,不包括格式。请阅读整个线程。恕我直言,这个问题描述得很好。
    猜你喜欢
    • 2016-01-27
    • 2020-08-06
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    相关资源
    最近更新 更多