我可以使用httpclient的访问器来解决这个问题,然后模拟httpmessagehandler。这是我使用的代码。
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
下面是测试用例的实现:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
这允许我将所需的响应传递到
SetupRestfulService
连同凭证和返回一个我可以调用函数的对象。这有点不太理想,但它避免了我必须充实整个适配器模式,并进入那个兔子洞。