And*_*rew 5 c# design-patterns winforms asp.net-web-api dotnet-httpclient
背景
我正在构建一个双层应用程序:
Winforms客户端将使用WebAPI服务HttpClient
.两层都大量使用IoC和依赖注入设计模式
题
当Winforms应用程序需要来自WebAPI服务的数据时,演示者将协调请求.我的问题是,你会HttpClient
直接在演示者内部使用吗?为了保持演示者的可测试性,您如何确保不必依赖具体的HttpClient
电话?我想在某种程度上也整合了这个问题的最佳答案.
我通过抽象一切来解决这个问题.
在表示层我会有一个服务抽象...
public interface IServiceAgent {
Task<SomeResultObject> GetSomething(string myParameter);
}
Run Code Online (Sandbox Code Playgroud)
...从Web API中抽象出我想要的东西.演示者不需要协调请求.演示者不关心数据来自何处.它所知道的是它需要一些东西并要求它(SoC).这是服务代理的工作(SRP).
服务代理实现可能需要调用不同的数据源.包括网络.因此抽象HttpClient
将放松与该实现的耦合.
一个简单的例子,如......
public interface IHttpClient {
System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class;
System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class;
//...other members as needed : DeleteAsync, PostAsync, PutAsync...etc
}
Run Code Online (Sandbox Code Playgroud)
一些示例实现可能看起来像这样......
public class MyPresenter {
public MyPresenter(IServiceAgent services) {...}
}
public class MyDefaultServiceAgent : IServiceAgent {
IHttpClient httpClient;
public MyDefaultServiceAgent (IHttpClient httpClient) {
this.httpClient = httpClient;
}
public async Task<SomeResultObject> GetSomething(string myParameter) {
var url = "http://localhost/my_web_api_endpoint?q=" + myParameter;
var result = await httpClient.GetAsync<SomeResultObject>(url);
return result;
}
}
public class MyDefaultHttpClient : IHttpClient {
HttpClient httpClient; //The real thing
public MyDefaultHttpClient() {
httpClient = createHttpClient();
}
/// <summary>
/// Send a GET request to the specified Uri as an asynchronous operation.
/// </summary>
/// <typeparam name="T">Response type</typeparam>
/// <param name="uri">The Uri the request is sent to</param>
/// <returns></returns>
public System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class {
return GetAsync<T>(new Uri(uri));
}
/// <summary>
/// Send a GET request to the specified Uri as an asynchronous operation.
/// </summary>
/// <typeparam name="T">Response type</typeparam>
/// <param name="uri">The Uri the request is sent to</param>
/// <returns></returns>
public async System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class {
var result = default(T);
//Try to get content as T
try {
//send request and get the response
var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
//if there is content in response to deserialize
if (response.Content.Headers.ContentLength.GetValueOrDefault() > 0) {
//get the content
string responseBodyAsText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
//desrialize it
result = deserializeJsonToObject<T>(responseBodyAsText);
}
} catch (Exception ex) {
Log.Error(ex);
}
return result;
}
private static T deserializeJsonToObject<T>(string json) {
var result = JsonSerializer.Deserialize<T>(json);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
通过抽象这些依赖关系,您可以通过允许使用伪造/模拟服务代理进行单元测试来保持演示者可测试.您可以使用假/模拟HTTP客户端测试服务代理.如果需要更改/交换/维护应用程序组件,它还允许您注入这些接口的任何具体实现.
归档时间: |
|
查看次数: |
1414 次 |
最近记录: |