Spring Boot 控制器不响应 POST 请求。

11

我正在使用具有云功能的收据打印机。它与我正在实现的服务器规范通信。它每x秒轮询一个带有POST请求的URL,当POST响应包含特定信息时,打印机会发送GET请求到同一URL获取要打印的信息。

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

我的问题是来自打印机的POST请求从未到达控制器。然而,我能够从Postman发送一个POST请求到完全相同的URL,并且该请求可以被控制器处理。

URL只是: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更新以下信息--- 由于我有一个常规的Spring(非boot)应用程序,可以接受来自打印机的POST请求,因此我可以记录传入请求中的信息。所以我做了这个。

这是来自打印机的一个POST请求,但Spring Boot控制器没有接受它:

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. 内容长度标头不同。 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,我发送了一个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,则尝试使用http://localhost:8080/cloudprint进行POST调用并检查。 - Sambit
在DEBUG模式下,日志通常会打印出监听器正在处理哪些URL端点。你可能想从那里开始。 - BillMan
@sambit:我无法让打印机调用本地主机。 - Mats Andersson
@sambit 打印机在云端,无法访问我的本地主机。 - Mats Andersson
1
@MatsAndersson,你是否拥有有效的SSL证书,还是使用自签名证书?Spring Boot是否进行了SSL终止,或者你使用了前置代理? - helospark
显示剩余14条评论
4个回答

9
如果您使用的是Spring Boot 2,则可能是由于csrf保护引起的。它仅影响非GET请求,并且在Spring Boot 2上默认启用,但在早期版本中则关闭。
因此,它很好地反映了您的问题描述 - 尽管我不完全理解为什么可以通过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
@Mezaine:根本问题在于我无法控制打印机发送的内容。它是一台现成的打印机,具有内置的云打印功能。 - Mats Andersson
@mats-andersson 抱歉,我没有时间查看打印机如何发送POST请求。但是也许可以使用过滤器来检查POST请求是否到达目的地:如果没有正文内容(RequestBody为空),则可能会被拒绝,但是必须抛出异常(您没有将属性“required”设置为false,是吗?)。 - Meziane
@Mezaine 我不理解这里的逻辑。您如何解释从Postmaster发布到完全相同的URL,但控制器成功接收了它? - Mats Andersson
我已经设置了:<logger name="org.springframework" level="debug">,但我仍然看不到这些POST请求的日志。它们甚至没有到达Spring... - Mats Andersson
显示剩余8条评论

3

您正在通过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存储中。有关如何执行此操作的详细的逐步指南,请单击此处。

使用带有标题的REST客户端时出现ssl.SSLHandshakeException,但在PostMan中正常工作


是的,这正是我怀疑的。Let's Encrypt证书真的不被认为是可以接受的吗?那就是我所拥有的。 - Mats Andersson

2
每当Java尝试通过SSL连接到另一个应用程序时(例如:HTTPS,IMAPS,LDAPS),只有在它信任该应用程序时,它才能连接到该应用程序。在Java世界中处理信任的方式是使用密钥库(通常为$JAVA_HOME/lib/security/cacerts),也称为信任库。其中包含所有已知的证书颁发机构(CA)证书列表,Java仅信任由这些CA之一签名或存在于该密钥库中的公共证书所签名的证书。因此,此问题可能由自签名的证书(未经CA签名)或不存在于Java信任库中的证书链引起。它不信任该证书并无法连接到应用程序。最初的回答。

2
我不确定您在发出POST请求时使用的格式。您可以使用Postman来验证您的应用程序能够处理哪种类型的POST请求。然后发送相同类型的POST请求,我相信您可以轻易地为每个请求找到示例代码。通常情况下,当我们从HTML页面提交POST时,我们使用x-www-form-urlencoded格式。

post methods from post man


它来自云打印机,我无法访问其代码,只有他们的文档,其中并未涵盖该格式。请阅读整个线程。在我看来,问题已经被很好地描述了。 - Mats Andersson

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