如何在 Spring Cloud Gateway 的 filter 中返回 JSON
概述
在 gateway GlobalFilter 或者 GatewayFilter 中往往可能需要拦截请求并提前返回一个 JSON 给客户端,就拿鉴权来说,如果请求未携带 token 或者携带的 token 有误,那么就要拦截请求并给客户端返回 401,并根据业务需要返回一段 JSON 给客户端。本文将讲述在 filter 中如何返回 JSON 给客户端。
具体实现
一个 Http 响应通常涉及以下三个方面内容:
- 状态码
- 响应头
- 响应体
只要将这三部分搞定了,我们就能够完成任何响应,至于 JSON 响应只不过是 Content-Type 这个响应头的值必须是 application/json 而已。
下面我们就以 AuthFilter 为例,假设请求没有携带 token,我们需要给客户端响应 401,响应内容为:
{ "msg": "token required" }
AuthFilter 需要实现的如下:
@Component
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 设置状态码
// 设置响应头
// 设置响应体
}
}
1、设置状态码
不同的状态码代表不同的含义,一般用 401(unauthorized)来告诉客户端的凭证不正确或者缺失凭证。这个过程很简单,我们只需要从 exchange 中获取 response 对象,然后给 response 设置状态码即可:
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
2、设置响应头
response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
3、设置响应体
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> data = new HashMap<>();
data.put("msg", "token required");
String body = "";
try {
body = mapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer buf = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buf));
抽象一个方法来处理 JSON 响应
上面三步都很简单,但是如果组合在一起代码量却不少,这些都是样板化的代码,所以很有必要抽象一个方法来干这个事情,我们可以将状态码和响应数据作为参数传进来就可以了。
public static Mono<Void> jsonResponse(ServerHttpResponse resp, HttpStatus status, Object data) {
String body;
try {
body = new ObjectMapper().writeValueAsString(data);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
resp.setStatusCode(status);
resp.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
DataBuffer buffer = resp.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return resp.writeWith(Flux.just(buffer));
}
有了这个方法后,我们再来实现 AuthFilter 就很简单了:
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Map<String, Object> data = new HashMap<>();
data.put("msg", "token required");
return jsonResponse(exchange.getResponse(), HttpStatus.UNAUTHORIZED, data);
}
当你把代码 copy 到 idea 中之后,发现 writeValueAsString 上面有个扎眼的黄色,鼠标移上去还有一个提示“Inappropriate blocking method call ”,啥意思呢,难道将一个对象序列化为 json 字符串还会阻塞吗?
~~这个我会在后面的文章中讲解,暂时就留个坑在这吧。~~先提前说明一下,这个黄色并无实际影响,可以放心使用。另外如果要说这个实现有瑕疵,那就是每次都创建一个 ObjectMapper,大家可以将 ObjectMapper 传进来,或者通过容器获取 ObjectMapper。
欲知详情,可以参考:idea: Inappropriate blocking method call
温馨提示:反馈需要登录