全局响应处理

85 阅读2分钟

本节视频教程链接

大家发现没有,前面我们在实现UserController时,API的方法签名返回值用的是统一的Response,在返回时,都要自己调一下Response.ok(...)。我们可不可以就按照service的返回结果,来和controller的返回值实现统一呢?这一节我们就来探究下。

API示例

现在我们抛开之前的UserAPI,写一个TestAPI来做试验:

package com.xiaojuan.boot.web.api;

import ...

@RequestMapping("test")
public interface TestAPI {

    @GetMapping("msg")
    String msg();

    @PostMapping("post")
    void post(HttpServletResponse response);

    @PostMapping("post2")
    void post2();
}

TestController实现非常简单,这里就不贴出来了。

再写一个单元测试:

package com.xiaojuan.boot.web.controller;

import ...

public class TestControllerTest extends WebTestBase {

    @Test
    public void test() {

        get("/test/msg", String.class);

        postForm("/test/post", Void.class, null);

        postForm("/test/post2", Void.class, null);
    }

}

实现ResponseBodyAdvice接口

为了达到全局统一response的目的,我们可以实现一个ResponseBodyAdvice接口,看下代码:

package com.xiaojuan.boot.common.web.support;

import ...

@Slf4j
@RestControllerAdvice
public class RestBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 不过滤,对所有RestController都应用
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        log.info("======= url = {}, body = {}", request.getURI(), body);
        return null;
    }
}

运行单元测试,看打印的日志:

image.png

很显然,我们发现当我们设计的API中有注入HttpServletResponse类型的响应对象时,这种响应体的拦截机制不管用了。这是我们在使用这种方式处理统一响应要特别注意的地方!这种情况下,我们可以将返回值改为String返回""即可。

@Override
public String post(HttpServletResponse response) {
    return SysConst.EMPTY_STRING;
}

image.png

然后,我们完善下解析方法:

@Override
public Object beforeBodyWrite(...) {
    if (body instanceof String) {
        // 字符串需要手动序列化为json
        response.getHeaders().set("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        return "{\"status\":0, \"data\": \"" + body + "\"}";
    }
    return Response.ok(body);
}

改造UserAPI返回值

现在再来看下UserAPI的返回值的调整:

image.png

UserController中相应的调整这里就不贴出来了。我们满怀期待的跑下UserControllerTest,发现第一个单元测试就不通过,通过断点调试,我们发现在经过全局异常处理后我们已经包装成了Response,此时在全局响应处理方法中,我们应当直接返回,不应该再包装一层:

image.png

调整为:

@Override
public Object beforeBodyWrite(...) {
    if (body instanceof String) {
        ...
    } else if (body instanceof Response) {
        return body;
    }
    return Response.ok(body);
}

最后我们再跑一次UserControllerTest,ok!搞定!

image.png

我们的改造又一次经受住了考验,圆满完成任务!