使用浏览器调用服务是处理测试的一种简单方法。客户端常常使用JavaScript(这是JSON的优点)和.NET客户端。下面创建一个Console App(.NET Core)项目来调用服务。
BookServiceClientApp的示例代码使用了以下依赖项和名称空间:
依赖项
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Loggingh.Console
Newtonsoft.Json
名称空间
Microsoft.Extensions.Logging
Newtonsoft.Json
System
System.Collections.Generic
System.Linq
System.Net.Http
System.Net.Http.Headers
System.Runtime.CompilerServices
System.Text
System.Threading.Tasks
System.Xml.Linq
1. 发送GET请求
要发送HTTP请求,应使用HttpClient类。在本章中,HttpClient类用来发送不同的HTTP请求。要使用HttpClient类,需要添加NuGet包System.Net.Http,打开名称空间System.Net.Http。要将JSON数据转换为.NET类型,应添加NuGet包Newtonsoft.Json。
为了把需要的所有URL放在一个地方,UrlService类为需要的URL定义了属性:
public class UrlService
{
public string BaseAddress => "https://localhost:5001";
public string BookaPI => "api/BookChapters/";
}
注意:
需要将UrlService类中的BaseAddress更改为服务的主机和端口号。当启动服务主机时,可以在浏览器中看到端口号。
在示例项目中,泛型类HttpClientService创建为对于不同的数据类型只有一种实现方式。构造函数需要通过DI获得UrlService,使用从UrlService中检索的基地址创建HttpClient:
public class HttpClientService<T>:IDisposable where T:class
{
private HttpClient _httpClient;
private readonly UrlService _urlService;
private readonly ILogger<HttpClientService<T>> _logger;
public HttpClientService(UrlService urlService,ILogger<HttpClientService<T>> logger)
{
_urlService = urlService ?? throw new ArgumentNullException(nameof(urlService));
_logger = logger ??throw new ArgumentNullException(nameof(logger));
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(urlService.BaseAddress);
}
public void Dispose()
{
_httpClient.Dispose();
}
}
注意:
在示例代码中,ILogger接口用于向控制台写入日志信息。
方法GetInternalAsync发出一个GET请求来接收一组项。该方法调用HttpClient的GetAsync方法来发送GET请求。HttpResponseMessage包含收到的信息。响应的状态码写入控制台来显示结果。如果服务器返回一个错误,则GetAsync方法不抛出异常。异常在方法EnsureSuccessStatus中抛出,该方法在返回的HttpResponseMessage实例上调用。如果HTTP状态码是错误类型,该方法就抛出一个异常。响应体包含返回的JSON数据。这个JSON信息读取为字符串并返回:
private async Task<string> GetInternalAsync(string requestUri)
{
if (string.IsNullOrEmpty(requestUri))
{
throw new ArgumentNullException(nameof(requestUri));
}
if (_objectDisposed)
{
throw new ObjectDisposedException(nameof(_httpClient));
}
HttpResponseMessage resp = await _httpClient.GetAsync(requestUri);
LogInformation($"status from GET {resp.StatusCode}");
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
private void LogInformation(string message,[CallerMemberName]string callerName = null)
{
_logger.LogInformation($"{nameof(HttpClientService<T>)}.{callerName}: {message}");
}
服务器控制器用GET请求定义了两个方法:一个方法返回所有章,另一个方法只返回一个章。但是需要章的标识符和URL。方法GetAllAsync调用GetInternalAsync方法,把返回的JSON信息转换为一个集合,而方法GetAsync将结果转换成单个项这些方法声明为虚拟的,允许在派生类中重写它们:
public async virtual Task<T> GetAsync(string requestUri)
{
if (string.IsNullOrEmpty(requestUri))
{
throw new ArgumentNullException(requestUri);
}
string json = await GetInternalAsync(requestUri);
return JsonConvert.DeserializeObject<T>(json);
}
public async virtual Task<IEnumerable<T>> GetAllAsync(string requestUri)
{
if (string.IsNullOrEmpty(requestUri))
{
throw new ArgumentNullException(nameof(requestUri));
}
string json = await GetInternalAsync(requestUri);
return JsonConvert.DeserializeObject<IEnumerable<T>>(json);
}
在客户端代码中不使用泛型类HttpClien