【问题标题】:Spring REST Controller is not responding to Angular requestSpring REST Controller 没有响应 Angular 请求
【发布时间】:2019-05-25 21:09:27
【问题描述】:

我有一个应用程序来创建服务器证书请求,就像使用 java keytool 之类的一样。我正在尝试在 zip 文件中返回创建的证书请求和密钥,但对于我的生活,我无法让我的 REST 控制器响应 http 请求。更正:控制器响应,但方法中的代码从未执行。

服务器确实收到了请求,因为我的 CORS 过滤器已执行。但是我在控制器方法中有一个调试集,它从未被触发。方法的签名是否正确?请问我需要另一双眼睛吗?

这是我的控制器代码:

@RequestMapping(method = RequestMethod.POST, value = "/generateCert/")
public ResponseEntity<InputStreamResource> generateCert(@RequestBody CertInfo certInfo) {
    System.out.println("Received request to generate CSR...");

    byte[] responseBytes = commonDataService.generateCsr(certInfo);
    InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(responseBytes));

    System.out.println("Generated CSR with length of " + responseBytes.length);
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=certificate.zip")
            .contentType(MediaType.parseMediaType("application/zip"))
            .contentLength(responseBytes.length)
            .body(resource);
}

这里是 Angular 请求:

generateCertificate(reqBody: GenerateCert) {
   let headers = new Headers();
   headers.append('Content-Type', 'application/json');

   this.http.post(this.urlGenerateCert, JSON.stringify(reqBody), {headers: headers}).subscribe(
    (data) => {
        let dataType = data.type;
        let binaryData = [];
        binaryData.push(data);
        this.certBlob = new Blob(binaryData);
    });
    return this.certBlob;
 }

最后,我从网络面板复制的请求和响应标头:

Response
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, X-Requested-With, remember-me
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Thu, 27 Dec 2018 22:48:00 GMT
Expires: 0
Location: http://localhost:8102/login
Pragma: no-cache
Set-Cookie: JSESSIONID=EDACE17328628D579670AD0FB53A6F35; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

Request
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 205
Content-Type: application/json
Host: localhost:8102
Origin: http://localhost:4200
Referer: http://localhost:4200/generateCerts
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36

我真的很难让 CORS 正常工作,所以这可能会干扰请求?除非绝对必要,否则我讨厌发布所有代码。有人有什么想法吗?

【问题讨论】:

  • 我们又来了。 http.post 返回一个 Observable。不是一个斑点。为什么?因为 AJAX 是异步的。所以它返回一个 observable,它允许你在很久以后,当响应最终可用时得到通知。当您执行this.certBlob 时,您会在发送请求后立即执行此操作。回应还没有回来。所以 this.certBlob 是未定义的。服务必须返回一个 observable。对响应感兴趣的组件必须订阅返回的 observable,以便在证书返回时收到通知。
  • 嗯,我相信我确实订阅了请求 - 滚动过去。
  • 您确实订阅了。在您订阅并因此发送请求之后,您返回 this.certBlob,它是未定义的,因为它只会在很久以后,在方法返回很长时间之后,当回调传递给订阅时填充已被调用,因为响应终于从服务器返回。将吐司放入烤面包机后,您不能立即食用。你需要等到烤面包机告诉你吐司烤好了。
  • 我已经告诉过你了。阅读我的第一条评论。服务必须返回一个可观察的。 IE。 return this.http.post(...); 调用 te 服务并想要访问证书的组件必须订阅服务返回的 observable。不要订阅服务。
  • 为什么不使用 REST 客户端,例如邮递员首先测试端点。这将有助于准确了解问题出在 Angular 应用程序还是 spring-boot 应用程序上。

标签: spring-mvc spring-boot spring-restcontroller


【解决方案1】:

请求/响应标头列表缺少有关 URL、方法和最重要的响应状态代码的信息。

在响应标头中看到Location: http://localhost:8102/login,我可以猜测它可能是401 Unauthorized 或其他任何重定向到登录页面的东西。因此,如果过滤器链中存在 auth 过滤器,则可能是罪魁祸首。

以下请求标头

Host: localhost:8102
Origin: http://localhost:4200

建议您正在执行 CORS,并且可能确实涉及 CORS 过滤器并在请求被路由到控制器之前完成响应。我建议在 CORS 过滤器中设置一个断点(如果有的话,也可以在其他过滤器中设置一个断点)并将其调试到返回响应的位置。

【讨论】:

  • 是的,这正是我所做的,我发现请求正在发送到服务器,但不是我的控制器方法。
【解决方案2】:

定义一个proxy.conf.json

{
"/login*": {
    "target":"http://localhost:8080",
    "secure":false,
    "logLevel":"debug"
    }
} 

现在在你的 package.json

"scripts": {
    "start":"ng serve --proxy-config proxy.config.json"
}

我认为在两个 webapp 中获取连接时存在问题。请尝试。

【讨论】:

    【解决方案3】:

    当 Angular 遇到这个语句时

    this.http.post(url,body).subscribe(data => # some code
    );
    

    当服务继续执行时,它会立即返回运行其余代码。就像 Java 中的 Future。

    如果你在这里

    return this.cert;
    

    您不会获得最终可能由 this.http 服务填充的值。由于页面已经呈现并且代码已经执行。您可以通过在 Observable 内部和外部包含它来验证这一点。

    console.log(“Inside/outside observable” + new Date().toLocalTimeString());
    

    【讨论】:

      【解决方案4】:

      感谢所有做出贡献的人。我发现错误是由于我的控制器方法的标头造成的。更改它们后,该方法被正确调用。这是有效的:

      @RequestMapping(method = RequestMethod.POST, path = "/generateCert", 
          produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE}, consumes = {MediaType.APPLICATION_JSON_VALUE})
      public ResponseEntity<byte[]> generateCert(@RequestBody CertInfo certInfo) {
          byte[] responseBytes = commonDataService.generateCsr(certInfo);    
          return ResponseEntity.ok()
                  .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                  .contentLength(responseBytes.length)
                  .body(responseBytes);
      }
      

      【讨论】:

        猜你喜欢
        • 2018-11-17
        • 1970-01-01
        • 2020-08-09
        • 2011-06-08
        • 1970-01-01
        • 2018-03-06
        • 2021-10-13
        • 2016-04-24
        • 2019-10-17
        相关资源
        最近更新 更多