我正在尝试编写一个客户端库,HttpClient
用于向/从 Web 服务器发送和接收信息。这个库的核心是一个WebClient
类:
public class WebClient {
public String send(String apiUrl) {
// Use HttpClient to send a message to 'apiUrl', such as:
// http://example.com?a=1&response=xml
//
// Wait for a response, and extract the HTTP response's body out
// as raw text string. Return the body as a string.
}
}
再说一次,这是我正在编写的库mylib.jar
( )。一个由各种不同服务组成的库,其中每个服务都有 1+ 方法,API 开发人员可以使用这些方法将数据读/写到服务器。所以服务如下:
WidgetService
getAllWidgets
getMostExpensiveWidget
getLastWidgetUpdated
etc...
FizzService
getFizzById
getFizziestFizz
etc...
BuzzService
etc...
...
每个服务方法将采用 1+ Java 原语或 1+ 模型实例(实体、值对象等)。每个服务方法都会返回一个这样的模型对象。例如:
public class FizzService {
public Fizz getFizzById(Long fizzId) {
// ...
}
public Fizz getFizzByBuzz(Buzz buzz) {
// ...
}
}
这一点很重要,因为这意味着WebClient#send()
最终需要将接收到的 HTTP 响应正文映射回 Java 对象。
从 API 开发人员的角度来看,我只希望开发人员实例化每个服务实例,将其传递给WebClient
后台使用,然后让服务完成参数和 api url 之间以及 HTTP 响应主体和模型/实体之间的所有映射。
例如:
public class FizzService {
private WebClient webClient;
private FizzApiBuilder fizzApiBuilder;
// Getters & setters for all properties...
public Fizz getFizzByBuzz(Buzz buzz) {
// apiCall == "http://example.com/fizz-service/getFizzByBuzz?buzz_id=93ud94i49&response=json"
String apiCall = fizzApiBuilder.build(buzz).toString();
// We asked API to send back a Fizz as JSON, so responseBody is a JSON String representing
// the correct Fizz.
String responseBody = webClient.send(apiCall);
if(apiCall.contains("json"))
return JsonFizzMapper.toFizz(reponseBody);
else
return XmlFizzMapper.toFizz(responseBody);
}
}
// What the API developer writes:
WebClient webClient = WebClientFactory.newWebClient();
FizzService fizzSvc = new FizzService(webClient);
Buzz b = getBuzz();
Fizz f = fizzSvc.getFizzByBuzz(b);
到目前为止,我喜欢这个设置。但是,对于所有服务中的每个服务方法,我都需要相同的“样板”代码:
String apiCall = someBuilder.build(...)
String responseBody = webClient.send(apiCall)
if(apiCall.contains("json"))
return JsonMapper.toSomething(responseBody)
else
return XmlMapper.toSomething(responseBody)
这开始闻起来像是抽象的主要用例。理想情况下,我希望将所有这些样板代码放在一个 中AbstractService
,并让每个服务扩展该抽象类。只有一个问题:
public abstract class AbstractService {
private WebClient webClient;
private ServiceApiBuilder apiBuilder;
// Getters & setters for all properties...
public Object invoke(Object... params) {
// apiCall == "http://example.com/fizz-service/getFizzByBuzz?buzz_id=93ud94i49&response=json"
// The injected apiBuilder knows how to validate params and use them to build the api url.
String apiCall = apiBuilder.build(params).toString();
String responseBody = webClient.send(apiCall);
if(apiCall.contains("json"))
return jsonMapper.to???(reponseBody);
else
return xmlMapper.to???(responseBody);
}
}
public class FizzService extends AbstractService { ... }
问题是,将功能抽象为一个使构建 API 调用的AbstractService
事情变得尴尬(但并非不可能),但更糟糕的是,我完全不知道如何创建和注入一组 JSON/XML 映射器,这些映射器将知道什么模型/要映射到的实体responseBody
。
这一切都开始发臭了。当我的代码发臭时,我会寻找解决方案,如果找不到,我会来这里。所以我问:我是否采取了根本错误的方法,如果是,我的方法应该是什么?如果我很接近,那么我如何注入正确的映射器并让这段代码闻起来更好?请记住我的代码片段显示了我希望 API 开发人员如何实际使用模型/服务 - 这是这里的最终目标。提前致谢。