angularjs - 通过 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 {}
}
解决方案
是的,当您尝试在不同服务器或同一服务器但具有不同端口地址之间进行通信时,就会发生这种情况。您需要在微服务端添加跨域配置。有时(发生在我身上)这个 @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;
}
就是这样,希望您的问题能够得到解决。
推荐阅读
- image-processing - 使用 Tesseract 从小图像中读取日期
- flutter - 如何在 Flutter 中使用增强现实?有人可以推荐我任何课程或视频吗?
- python - 如何在常规 chrome 而不是 chromedriver 中运行 selenium 代码?
- postgresql - MAX(COUNT(...)) - 错误:不能嵌套调用聚合函数
- javascript - 是否可以在不同的系统/浏览器上具有相同 html 元素的不同 ID 属性?
- asynchronous - 我应该在服务器上的 WEB API 中使用 async/await 吗?
- javascript - 从 SQL Server 获取数据后重置记录集 - Angular
- postgresql - 我应该如何在我的 Postgres 数据库设计中处理父用户和子用户?
- bluetooth - 如何从 esp 32 classic-bluetooth 在我的手机中获取 pin 请求
- swift - 如何在单个@IBAction 中获取多个 UISlider 的值?