1,所需依賴
<!-- 網關依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2,配置路徑映射
spring:
cloud:
gateway:
discovery:
locator:
# 開啟項目名稱映射功能,即通過服務名訪問項目,不需要寫明具體IP地址
enabled: true
# 請求路由全部轉小寫字母,如 Aa/Bb 會被轉為 aa/bb
lower-case-service-id: true
routes:
# 路由規則的唯一標識,任意齊名,建議和服務名一致
- id: sc-web-admin-back
# 轉發到的節點,lb:// 為 http,lb:ws:// 為 websocket
uri: lb://sc-web-admin-back/
predicates:
# 斷言規則,這里設置匹配的路由規則
- Path=/admin-back/**
# 過濾規則
filters:
# 轉發到具體項目去掉路由首單詞,如 /admin-back/a 轉發為 /a
- StripPrefix=1
如上,便配置了一個接管以 admin-back 開頭的路由,並轉發給項目 sc-web-admin-back
3,跨域配置
spring:
cloud:
gateway:
globalcors:
# 全局允許跨域訪問
cors-configurations:
'[/**]':
allow-credentials: true
allowed-origins: "*"
allowed-headers: "*"
allowed-methods:
- OPTIONS
- GET
- POST
- PUT
- DELETE
4,全局異常處理
日常開發中,我們難免會有忘記處理的異常,程序也很難保重不出錯,這時候我們就需要一個能夠全局捕捉異常的功能來未我們兜底,防止錯誤被用戶看到影響體驗
package com.hwq.gateway.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hwq.gateway.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
@Component
@Primary // 同類型帶有該注解的 Bean 優先背識別
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class JsonExceptionHandler implements WebExceptionHandler {
@Autowired
private ObjectMapper objectMapper;
/**
* 所有異常都會在這里被集中處理
* @param exchange 上下文
* @param ex 異常
* SuppressWarnings("NullableProblems"):屏蔽 @NonNullApi 的警告
*/
@Override
@SuppressWarnings("NullableProblems")
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
ResultVO<Object> vo;
if (ex instanceof ResponseStatusException && ((ResponseStatusException) ex).getStatus() == HttpStatus.NOT_FOUND) {
vo = ResultVO.fail("當前訪問的地址不存在,請修改后重試!");
} else {
vo = ResultVO.error(ex.getMessage());
}
return response.writeWith(Mono.fromSupplier(() -> {
byte[] bytes;
try {
bytes = objectMapper.writeValueAsBytes(vo);
} catch (JsonProcessingException e) {
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
bytes = "JSON 轉換異常!".getBytes();
}
return response.bufferFactory().wrap(bytes);
}));
}
}
5,捕獲轉發項目傳來的異常
上面的類只能捕捉網關本身發生的異常,而如果其調用(轉發)的項目發生異常,返回錯誤時不能攔截,這時候我們就需要一個過濾器,能把哪些錯誤的返回攔截掉,並觸發上面全局異常處理
package com.hwq.gateway.filter;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
@Component
public class ExceptionFilter implements Ordered, GlobalFilter {
/**
* 如果需要攔截返回值並處理,這里則需要返回負數
*/
@Override
public int getOrder() {
return -1;
}
/**
* 異常攔截,攔截響應值不是 200 的數據,並拋給全局異常處理
*/
@Override
@SuppressWarnings({"unchecked", "NullableProblems"})
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse response = exchange.getResponse();
ServerHttpResponseDecorator decorated = new ServerHttpResponseDecorator(response) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
if (response.getStatusCode() != HttpStatus.OK) {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer); // 釋放掉內存
String errMsg = new String(content, StandardCharsets.UTF_8);
throw new RuntimeException(errMsg);
}
return dataBuffer;
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decorated).build());
}
}
6,普通的過濾器
package com.hwq.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class UserFilter implements Ordered, GlobalFilter {
/**
* 這里的返回值決定攔截器的優先級,數字越小越先被觸發,支持負數
*/
@Override
public int getOrder() {
return 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 這里可以獲取 請求對象 和 響應對象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 邏輯處理
// 邏輯處理
// 傳遞給下一級別的過濾器
return chain.filter(exchange);
}
}