是要以互通性作为核心因素进行技术选型并构建一套开放繁荣的生态体系,还是以性能为核心因素进行技术选型构建一套封闭高效的生态体系,需要大家灵活把握,而本节我们将更多以 Web API 的形式,向大家展示如何基于 SpringBoot 构建一套开放、互通、稳定的 Web API 微服务体系。
使用 SpringBoot 构建 Web API 有几种选择,要么使用 spring-boot-starter-jersey 构建 RESTful 风格的 Web API,要么选择 spring-boot-starter-hateoas 构建更加有关联性和相对“智能”的 Web API,但笔者认为这些都有点儿“阳春白雪”。
对于大部分开发人员来说,HTTP 协议的 GET 和 POST 是直觉上最自然的选择,所以,我们选择使用最“下里巴人”的方式来构建 Web API。
Web API 强调统一和互通,所以,首先我们需要定义一套内外认知一致的 Web API 开发和访问规范,在 JSON 盛行、社群庞大的背景下,我们的 Web API 方案采用 JSON 作为数据交互格式并定义统一的协议格式,然后通过 HTTP 以及周边支持完成微服务的对外服务和开放访问。
1. 定义 Web API 规范
首先从服务访问的交互上来说,我们可以选择较为纯粹的 JSON RPC Over HTTP 的方式,如图 1 所示。
图 1 JSON RPC Over HTTP 示意图
也可以选择约束相对松一些的 RPC Over HTTP 方式,如图 2 所示。
图 2 一般意义上基于HTTP的RPC交互示意图
相对于纯粹的 JSON RPC Over HTTP 方案,后者对请求格式不做任何限制(所以也同样支持纯粹 JSON 形式的请求格式),只对响应(Response)做 JSON 格式上的统一规定。
好处是,客户端各种工具都能够很好的支持,服务器端 SpringMVC 也可以少做 HttpMessage 转换,给服务的开发者和访问者都提供了比较灵活的操作余地,至于请求的类型差异,我们可以通过配套生成的 API 文档进行补足。
整个 Web API 的功能流程算是跑通了,但跟我们之前定义的 Web API 规范却没有关系,所以,下一步我们要做的事情就是在此基础上规范 HTTP 响应格式,使其遵循我们之前定义的 Web API 规范,从而任何访问我们提供的 Web API 访问者都可以相同的认知使用这些 Web API,进而也可以打造和沉淀相应的工具或者类库。
我们定义的 Web API 规范并非最优,也并非必要,如果团队成员的认知差不多,那么直接使用 HTTP Status Code 结合直接的值类型响应就可以了,我们给出的 Web API 规范考虑了更多因素后做出的一个折中方案,但任何方案设计是否完美并非最主要的,执行才是。
要开发符合我们的 Web API 规范的 Web API,最少有两种方案可以选择:
显式的强类型封装方式(explicit type wrapper)
隐式的自动转换方式(implicit conversion)
显式的强类型封装方式的出发点是说,既然 spring-boot-starter-web 已经提供了 MappingJackson2HttpMessageConverter 用于对象类型到 JSON 的类型转换,那么,我们只要提供针对 Web API 规范的 Java 对象类型作为所有 Web API 处理方法的返回值就可以了,比如:
public class WebApiResponse {
public static final int SUCCESS_CODE = 0;
public static final int ERROR_CODE = 1;
private int code;
private String error;
private T data;
// getters, setters, toString(), etc.
}
然后,所有的 Web API 的处理方法统一定义为返回 WebApiResponse 作为结果类型:
3)因为现在只有一个 HttpMessageConverter 处理单一类型的 Web 请求和响应,如果同一项目中有类似视图渲染的需求,则无法满足需求。
所以,为了能够不打破开发者对 SpringMVC 框架以及 SpringBoot 提供的 Web 应用各项功能支持的认知,最稳妥的做法是,在 SpringBoot 原有 Web 应用默认配置的基础上增加新的 HttpMessageConverter,专门处理 Web API 响应结果使其符合我们的 Web API 规范形式。
要达到这个目的,我们可以提供自定义的配置:
@Configuration
public class WebApiConfiguration extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List> converters) {
对于我们的 Web API 规范这个实现场景来说,如果想继续享受原有的 MappingJackson2HttpMessageConverter 提供的功能和配置,就不得不继承并覆写(Override)相应方法,而不是略过 MappingJackson2HttpMessageConverter,然后在另一个 HttpMessageConverter 中只是必要的时候引用它(组合优于继承)。
不管怎么样,我们推荐使用隐式的自动转换方式为用户提供透明的 Web API 规范行为。
3. Web API 的短板和补足
相对于 Dubbo 这种强类型的服务框架,Web API 有强类型支持(Not Typesafe),在开发过程中,自然也无法享受到像 IDE 自动提示之类的功能,所以,对于 Web API 的使用者来说,需要与 Web API 的提供者沟通之后才能知道如何访问 Web API 的详细信息,比如应该传哪些参数,返回的响应结果又应该是什么格式的。
为了缓解这个问题,我们可以使用自动根据代码元信息生成 API 文档的方式来补足这块短板,像 Swagger 这样的项目,已经是比较成熟的 API 文档方案了。
不过,让每一个 Web API 项目都自己去初始化 API 文档相关的设置显然并不是很好的用户体验,为了服务到位,我们可以遵循 SpringBoot 的行事风格,新建一个 spring-boot-starter-webapi 这样的自动配置模块,其提供的主要特性包括但不限于:
提供针对我们 Web API 规范的功能支持,即提供显式的强类型封装方式或者隐式的自动转换方式的功能实现。
提供 API 文档相关功能的配置和设置。
提供统一的 Web API 访问错误处理逻辑。
这样,任何 Web API 的开发者和提供者只要新建 SpringBoot 应用,然后依赖 spring-boot-starter-webapi,就可以自动享有以上所有特性支持了。