通过 Google Compute Shell 部署 Spring Boot 服务和 UI 后出现 CORS 错误

Posted

技术标签:

【中文标题】通过 Google Compute Shell 部署 Spring Boot 服务和 UI 后出现 CORS 错误【英文标题】:Getting CORS error after deploying Spring boot service and UI through Google Compute Shell 【发布时间】:2021-05-17 17:29:21 【问题描述】:

我是 google cloud 的新手,我已经通过 Google Cloud Shell 部署了 Spring Boot 应用程序和 Angular JS 应用程序。 通过“java -jar MyAppName.jar &”命令启动 Spring Boot 应用程序,并使用“npm start”启动 UI 服务器。 UI 的 Web 预览 URL 是“https://8080-xx-123456789-default.region-zone.cloudshell.dev”,Spring 引导服务的休息端点是“https://8301-xx-123456789-default.region-zone.cloudshell.dev/maintenance”。当使用 Angular JS $http.post() 调用这个休息端点时,它会失败并且浏览器控制台会显示以下错误。

从源“https://8080-xx-123456789-default.region-”访问“https://8301-xx-123456789-default.region-zone.cloudshell.dev/maintenance”处的 XMLHttpRequest zone.cloudshell.dev' 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:预检请求不允许重定向。

8301-xx-123456789-default.region-zone.cloudshell.dev/maintenance:1 加载资源失败:net::ERR_FAILED

我在 npm start 命令中的 server.js 是:

var express= require('express');
var app=express();
app.use(express.static('myUIProjectName'));
app.get('/',function(req,res)
    res.redirect('/');
);

app.listen(process.env.port || 8080,'127.0.0.1');
console.log('UI server is listening on port 8080');

我的 Angular JS 控制器代码。

 var loginController = app.controller('auditController', function ($scope, $http, 
$rootScope, 
$location, $window, ServiceProcessor, fileUploadService, PageNavigatorService, 
DataTransferService, DownloadTransferService) 
$scope.uploadFile = function () 
    $scope.expenditureList = [];
    var file = $scope.myFile;
    var uploadUrl = "https://8301-xx-123456789-default.region- 
   zone.cloudshell.dev/maintenance";
    var promise = fileUploadService.uploadFileToUrl(file, uploadUrl);
    promise.then(function (response) 
        let calculationResponse = response.calculationResponse;
        $scope.calculationResponse = calculationResponse;
        let expenseNameToExpenseDetails = calculationResponse.expenseNameToExpenseDetails;
        for (const [expenseName, expenditure] of Object.entries(expenseNameToExpenseDetails)) 

            $scope.expenditureList.push(expenditure);
        
        DataTransferService.set(calculationResponse);
    , function (response1, response2) 
        $scope.serverResponse = 'An error has occurred';
    );
;

app.service('fileUploadService', function ($http, $q) 
this.uploadFileToUrl = function (file, uploadUrl) 
    //FormData, object of key/value pair for form fields and values
    var fileFormData = new FormData();
    fileFormData.append('file', file);

    var deffered = $q.defer();
    $http.post(uploadUrl, fileFormData, 
        transformRequest: angular.identity,
        // headers:  'Content-Type': undefined 
        headers: 'Content-Type': undefined,'withCredentials': true

    ).success(function (response) 
        deffered.resolve(response);

    ).error(function (response) 
        deffered.reject(response);
    );

    return deffered.promise;

);

我的 Spring 启动代码如下:

    @SpringBootApplication
    @EnableConfigurationProperties(
    FileStorageProperties.class
    )
 @ComponentScan(packages) 
public class EasymaintenanceApplication 

public static void main(String[] args) 
    SpringApplication.run(EasymaintenanceApplication.class, args);


@Bean
public WebMvcConfigurer corsConfigurer() 
    return new WebMvcConfigurer() 
        @Override
        public void addCorsMappings(CorsRegistry registry) 
            registry.addMapping("/**").allowedOrigins("https://8301-xx-123456789- 
   default.region-zone.cloudshell.dev","http://127.0.0.1:5500");
        
    ;
  


控制器代码:

@CrossOrigin(origins = "*")
@RestController
//@RequestMapping ("/maintenanceService")
public class MaintenanceController 

@Autowired
private MaintenanceBL maintenanceBL;

@Autowired
private FileStorageService fileStorageService;

private static final Logger logger = LoggerFactory.getLogger(MaintenanceController.class);

@CrossOrigin(origins = "*")
@PostMapping("/maintenance")
public UploadFileResponse calculateMaintenance(@RequestParam("file") MultipartFile file) 
throws Exception 
    UploadFileResponse response = null;
    try 
        String fileName = fileStorageService.storeFile(file);
        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/downloadFile/")
                .path(fileName)
                .toUriString();
        CalculationResponse calculationResponse = maintenanceBL.readMaintenanceFile(fileName);
        response = new UploadFileResponse(fileName, fileDownloadUri,
                file.getContentType(), file.getSize(), calculationResponse);
     catch (Exception e) 
        logger.error("Exception occurred while calculating maintenance", null, e);
    
    return response;
   

CORSHandler 类(doFilter 方法命中成功):

@Component
public class CORSHandler implements Filter 

public static final String X_CLACKS_OVERHEAD = "X-Clacks-Overhead";

@Override
public void doFilter(ServletRequest req, ServletResponse res,
        FilterChain chain) throws IOException, ServletException 
    System.out.println("Request is hitting");
    HttpServletResponse response = (HttpServletResponse) res;
    response.addHeader("Access-Control-Allow-Origin", "*");
    response.addHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
    response.addHeader("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User- 
   Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range");
    System.out.println("Request is chained");
    chain.doFilter(req, res);


@Override
public void destroy() 

@Override
public void init(FilterConfig arg0) throws ServletException 


【问题讨论】:

【参考方案1】:

请尝试在控制器中使用允许的来源之一,而不仅仅是“*”

就像在您的 corsConfigurer Bean 中一样,您已允许“https://8301-xx-123456789-default.region-zone.cloudshell.dev”和“http://127.0.0.1:5500”

   @Bean
   public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurer() 
           @Override
           public void addCorsMappings(CorsRegistry registry) 
           registry.addMapping("/**").allowedOrigins("https://8301-xx-123456789- 
           default.region-zone.cloudshell.dev","http://127.0.0.1:5500");
        
      ;
    

因此,请在您的控制器中使用以下来源之一进行检查,例如:

    @CrossOrigin(origins = "http://127.0.0.1:5500")
    @PostMapping("/maintenance")
    public UploadFileResponse calculateMaintenance(@RequestParam("file") MultipartFile 
           file) 
           throws Exception 
           UploadFileResponse response = null;
           try  ...

如果可行,那就太好了,问候!

【讨论】:

感谢@Prashant 回复。我尝试了上述解决方案,仍然遇到同样的问题。如果我需要分享我的代码中的更多信息,请告诉我。【参考方案2】:

是的,当您尝试在不同服务器或同一台服务器之间进行通信但端口地址不同时,就会发生这种情况。您需要在微服务端添加跨域配置。有时(发生在我身上)这个 @crossorigin 注释将被另一个配置覆盖,然后在这种情况下,您需要向 wite 列表添加一个全局过滤器,所有请求都进入您的 API。

在这里,我分享一个小代码sn-p来添加过滤器以消除跨域问题。

import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CORSFilter extends GenericFilterBean implements Filter 

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException 

    HttpServletResponse httpResponse = (HttpServletResponse) response;
    httpResponse.setHeader("Access-Control-Allow-Origin", "*");
    httpResponse.setHeader("Access-Control-Allow-Methods", "*");
    httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
    httpResponse.setHeader("Access-Control-Allow-Headers", "*");
    httpResponse.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, X-Auth-Token, X-Csrf-Token, Authorization");

    httpResponse.setHeader("Access-Control-Allow-Credentials", "false");
    httpResponse.setHeader("Access-Control-Max-Age", "3600");

    System.out.println("********* CORS Configuration Completed *********");

    chain.doFilter(request, response);
 

现在创建一个此类的 Bean,如下所示

@Bean
public FilterRegistrationBean corsFilterRegistration() 
    FilterRegistrationBean registrationBean =
            new FilterRegistrationBean(new CORSFilter());
    registrationBean.setName("CORS Filter");
    registrationBean.addUrlPatterns("/*");
    registrationBean.setOrder(1);
    return registrationBean;

希望您的问题能得到解决。

【讨论】:

感谢@Gaurav 回复。我尝试了上述解决方案,仍然遇到同样的问题。如果我需要分享我的代码中的更多信息,请告诉我。 @AmitSingh 请随时通过我的邮件 gaurav.mute@gmail.com 与我联系以获得进一步的帮助。 当然@Gaurav。谢谢。

以上是关于通过 Google Compute Shell 部署 Spring Boot 服务和 UI 后出现 CORS 错误的主要内容,如果未能解决你的问题,请参考以下文章

通过 R Studio Server 使用 BigQuery 在 Google Compute Engine 上对服务帐户进行身份验证

连接 Google App Engine 和 Google Compute Engine

Google Compute Engine:如何从外部提出请求?

在哪里可以找到 Google Compute Engine 的名称服务器?

Google的Container OS可以与Compute Engine上的gRPC一起使用吗?

是否可以将目录从 Google Compute Engine 实例复制到我的本地计算机?