You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过Spring Cloud Gateway转发POST请求时出现403 CORS错误,直接请求BFF微服务正常的问题求助

通过Spring Cloud Gateway转发POST请求时出现403 CORS错误,直接请求BFF微服务正常的问题求助

大家好,我现在正在用React + Spring Boot搭建一个微服务应用,系统里包含API网关、BFF层还有其他业务服务。最近碰到一个棘手的问题:当我通过网关转发POST请求到BFF时,总是返回403错误,但直接调用BFF的接口却完全正常。折腾了好一阵还是没找到原因,麻烦各位帮忙排查一下!

网关的application.properties配置

eureka.client.service-url.defaultZone=http://eureka:password@localhost:8761/eureka

spring.application.name=api-gateway

spring.main.web-application-type=reactive

logging.level.root=INFO

logging.level.org.springframework.cloud.gateway.route.RouteDefinitionLocator=INFO

logging.level.org.springframework.cloud.gateway=TRACE

spring.cloud.gateway.discovery.locator.enabled=true

spring.cloud.gateway.discovery.locator.lower-case-service-id=true

## BFF Service Route

spring.cloud.gateway.routes[0].id=bff

spring.cloud.gateway.routes[0].uri=http://localhost:8085

spring.cloud.gateway.routes[0].predicates[0]=Path=/**

spring.cloud.gateway.routes[0].predicates[1]=Method=GET,POST,DELETE, OPTIONS

## Order Service Route

spring.cloud.gateway.routes[1].id=order

spring.cloud.gateway.routes[1].uri=lb://order

spring.cloud.gateway.routes[1].predicates[0]=Path=/**

## Discovery Server Route

spring.cloud.gateway.routes[2].id=discovery-service

spring.cloud.gateway.routes[2].uri=http://localhost:8761

spring.cloud.gateway.routes[2].predicates[0]=Path=/eureka/web

spring.cloud.gateway.routes[2].filters[0]=SetPath=/

##Discovery Server Static Resources Route

spring.cloud.gateway.routes[3].id=discovery-service-static

spring.cloud.gateway.routes[3].uri=http://localhost:8761

spring.cloud.gateway.routes[3].predicates[0]=Path=/eureka/**

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8181/realms/spring-boot-microsevices-realm

网关的CorsConfig配置类

package com.mealkit.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig {

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")  // All endpoints in the application
.allowedOrigins("http://localhost:3000")  // Or other origins
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowedHeaders("*")// Or other HTTP methods
.allowCredentials(true);
}
}; }}

BFF的Product控制器代码(直接请求此处接口正常)

package com.mealkit.bff.web.product;

import com.mealkit.bff.api.model.product.request.ProductApiInput;
import com.mealkit.bff.api.model.product.response.ProductApiOutput;
import com.mealkit.bff.api.service.product.product.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/product")
@CrossOrigin(value = "http://localhost:3000")
public class ProductWebApi {

private final ProductService productService;

@GetMapping("/getProductById/{productId}")
public ResponseEntity<ProductApiOutput> getProductById(@PathVariable Long productId) throws Exception {
return productService.getProductById(productId);
}

@PostMapping(value = "/getProductsById")
public ResponseEntity<List<ProductApiOutput>> getProductsById(@RequestBody List<Long> productIdList) {
return productService.getProductsById(productIdList);
}

@PostMapping("/getProductsByTag")
public ResponseEntity<List<ProductApiOutput>> getProductByTag(@RequestBody List<Long> tagIdList) {
return productService.getProductByTag(tagIdList);
}

@DeleteMapping("/deleteProduct/{productId}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long productId) {
productService.deleteProduct(productId);
return new ResponseEntity<>(HttpStatus.OK);
}

@PutMapping("/updateProduct")
public ResponseEntity<Void> updateProduct(@RequestBody ProductApiInput productApiInput) {
productService.updateProduct(productApiInput);
return new ResponseEntity<>(HttpStatus.OK);
}

@PostMapping("/createProduct")
public ResponseEntity<Void> createProduct(@RequestBody ProductApiInput productApiInput) {
productService.createProduct(productApiInput);
return new ResponseEntity<>(HttpStatus.OK);
}

}

前端请求代码

const URL = "http://localhost:";
const PORT = "8080";
const ORDER_PORT = "8080";
const ORDER_ENDPOINT = "/api/v1/order";
const PRODUCT_ENDPOINT = "/api/product";
const TAG_ENDPOINT = "/api/tag";
const BUSINESS_ENDPOINT = "/api/business";

export const getProductById = async (productId) => {
try {
const response = await fetch(
`${URL}${PORT}${PRODUCT_ENDPOINT}/getProductById/${productId}`
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const getProductsByTag = async (tags) => {
try {
const response = await fetch(
`${URL}${PORT}${PRODUCT_ENDPOINT}/getProductsByTag`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tags),
}
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const getProductsById = async (productIdList) => {
try {
const response = await fetch(
`${URL}${PORT}${PRODUCT_ENDPOINT}/getProductsById`,
{
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(productIdList),
}
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const getTagsByIds = async (tagIdList) => {
try {
const response = await fetch(`${TAG_ENDPOINT}/getTagsByIds`, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(tagIdList),
});
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const getTagById = async (tagId) => {
try {
const response = await fetch(
`${TAG_ENDPOINT}/getTagById/${tagId}`
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const getTags = async () => {
try {
const response = await fetch(`${URL}${PORT}${TAG_ENDPOINT}/getTags`);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const businessGetProductById = async (productId) => {
try {
const response = await fetch(
`${URL}${PORT}${BUSINESS_ENDPOINT}/getProductById/${productId}`
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

export const placeOrder = async (products) => {
try {
const response = await fetch(
`${URL}${ORDER_PORT}${ORDER_ENDPOINT}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(products),
}
);
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
};

备注:内容来源于stack exchange,提问作者Yekta Yüksel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 11:24:34