首页 > 解决方案 > 通过 Google Compute Shell 部署 Spring Boot 服务和 UI 后出现 CORS 错误

问题描述

我是谷歌云的新手,我已经通过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-zone.cloudshell.dev”访问“https://8301-xx-123456789-default.region-zone.cloudshell.dev/maintenance”处的 XMLHttpRequest已被 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 {}

}

标签: angularjsspring-bootcors

解决方案


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

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

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;
}

就是这样,希望您的问题能够得到解决。


推荐阅读