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