开发通用的访问webapi方法。
在common工具文件夹下,新建一个类库项目:Wsk.Core.WebHelper,并引用Package包项目,然后新建一个类HttpClientHelper,用于使用HttpClient方法进行访问webapi:
新建一个接口IHttpClientHelper,用于HttpClientHelper继承该接口。然后接口内新增一个返回泛型类型的通用的POST访问webapi的方法:
接着,在HttpClientHelper类里面,进行对该方法的实现:
说明:虽然使用了using,可以自动释放资源;但是难免还是需要一点时间。在finally下面通过手动释放资源,比自动释放资源,资源释放率会更快,在并发场景下,性能会更好一点点。当然,此处可以不适用using,因为手动释放了,以上纯属个人喜好的风格写法。
再来一个使用Basic加密进行访问的通用方法,写法如上,具体请看代码示例。先新建带用户名和密码参数的接口:
然后,在HttpClientHelper里面进行对应的实现:
以上为使用POST的方式进行,如果需要使用例如GET、PUT等,可以自行尝试,写法类似。
接口代码:
public interface IHttpClientHelper { /// <summary> /// 通用访问webapi方法 /// </summary> /// <param name="url"></param> /// <param name="data"></param> /// <returns></returns> T Post<T>(string url, string data); /// <summary> /// 带用户名和密码的通用访问webapi方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="url"></param> /// <param name="data"></param> /// <param name="account">用户名</param> /// <param name="pwd">密码</param> /// <returns></returns> T Post<T>(string url, string data, string account, string pwd); }
实现类代码:
public class HttpClientHelper:IHttpClientHelper { readonly ILogger<HttpClientHelper> _logger; public HttpClientHelper(ILogger<HttpClientHelper> logger) { _logger = logger; } public T Post<T>(string url, string data) { var result = default(T); using (HttpClient client = new HttpClient()) { try { client.Timeout = new TimeSpan(0, 0, 10); // 10是秒数,用于设置超时时长 HttpContent content = new StringContent(data); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); client.DefaultRequestHeaders.Connection.Add("keep-alive"); Task<HttpResponseMessage> res = client.PostAsync(url, content); if (res.Result.StatusCode == System.Net.HttpStatusCode.OK) { result = JsonConvert.DeserializeObject<T>(res.Result.Content.ReadAsStringAsync().Result); } else { _logger.LogError($"访问webapi方法状态码错误:\r url:{url} \r data:{data} \r 状态码:{res.Result.StatusCode}"); } } catch (Exception ex) { _logger.LogError($"访问webapi方法异常:\r url:{url} \r data:{data} \r 异常信息:{ex.Message}"); } finally { client.Dispose(); } } return result; } public T Post<T>(string url, string data, string account, string pwd) { var result = default(T); using (HttpClient client = new HttpClient()) { try { string authorization = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(account + ":" + pwd)); client.DefaultRequestHeaders.Add("authorization", authorization); client.Timeout = new TimeSpan(0, 0, 10); HttpContent content = new StringContent(data); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); Task<HttpResponseMessage> res = client.PostAsync(url, content); if (res.Result.StatusCode == System.Net.HttpStatusCode.OK) { result = JsonConvert.DeserializeObject<T>(res.Result.Content.ReadAsStringAsync().Result); } else { _logger.LogError($"访问带用户名参数的webapi方法状态码错误:\r url:{url} \r data:{data} \r 状态码:{res.Result.StatusCode}"); } } catch (Exception ex) { _logger.LogError($"访问带用户名参数的webapi方法异常:\r url:{url} \r data:{data} \r 异常信息:{ex.Message}"); } finally { client.Dispose(); } } return result; } }
现在再新建一个使用HttpWebRequest的通用访问webapi的方式。在WebHelper项目下面,新建 HttpWebRequestHelper类,以及IHttpWebRequestHelper接口:
在接口里面,新建一个通用的泛型类型的访问webapi的请求接口:
然后,在HttpWebRequestHelper类里面,进行有关的实现:
HttpWebRequest没有Dispose方法,所以此处没有使用using写法,最后需要手动使用Abort方法进行释放资源。
接口代码:
public interface IHttpWebRequestHelper { /// <summary> /// 通用方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="url"></param> /// <param name="data"></param> /// <param name="method">默认POST</param> /// <returns></returns> T Request<T>(string url, string data, string method = "POST"); }
接口实现代码:
public class HttpWebRequestHelper:IHttpWebRequestHelper { readonly ILogger<HttpWebRequestHelper> _logger; public HttpWebRequestHelper(ILogger<HttpWebRequestHelper> logger) { _logger = logger; } public T Request<T>(string url, string data,string method="POST") { var result = default(T); HttpWebRequest request = null; try { request = (HttpWebRequest)WebRequest.Create(url); var byteData = Encoding.UTF8.GetBytes(data); request.Method = method; request.ContentType = "application/json"; request.ContentLength = data.Length; request.Timeout = 10000; // 超时时间,毫秒单位 using (var stream = request.GetRequestStream()) { stream.Write(byteData, 0, data.Length); } var response = (HttpWebResponse)request.GetResponse(); // 发送请求 using (var resStream = response.GetResponseStream()) // 读取数据 { using (var reader = new StreamReader(resStream, Encoding.UTF8)) { result = JsonConvert.DeserializeObject<T>(reader.ReadToEnd()); } } } catch (Exception ex) { _logger.LogError($"使用HttpWebRequest访问webapi方法异常:\r url:{url} \r data:{data} \r 异常信息:{ex.Message}"); } finally { if (request != null) { request.Abort(); // 释放资源 } } return result; } }
现在开发两个webapi进行测试。首先把该类库项目,添加到启动项目的项目引用里面。然后,在启动项目里面的AutofacRegister里面,添加对Wsk.Core.WebHelper类库项目的所有接口进行依赖注入注册:
注册部分代码:
var assemblysWebhelper = Assembly.Load("Wsk.Core.WebHelper"); // container.RegisterAssemblyTypes(assemblysWebhelper) .SingleInstance() .AsImplementedInterfaces() .EnableInterfaceInterceptors();
新建一个实体类,用来当作参数和返回值的测试:
接着,在控制器里面写几个测试方法进行测试,测试内容如下:
控制器部分代码:
[Route("[controller]/[action]")] [ApiController] public class WSKController : ControllerBase { private readonly ITestAutofac _autofac; private readonly ILogger<WSKController> _logger; private readonly IRedisManage _redis; private readonly IHttpClientHelper _httpClient; private readonly IHttpWebRequestHelper _httpWebRequestHelper; public WSKController(ITestAutofac autofac, ILogger<WSKController> logger, IRedisManage redis, IHttpClientHelper httpClient, IHttpWebRequestHelper httpWebRequestHelper) { _autofac = autofac; _logger = logger; _redis = redis; _httpClient = httpClient; _httpWebRequestHelper = httpWebRequestHelper; } [HttpPost] public IActionResult HelloWorld(string url1,string url2) { TestWebHelperInfo info = new TestWebHelperInfo(); info.name = "Hello"; var value1 = _httpClient.Post<TestWebHelperInfo>(url1,JsonConvert.SerializeObject(info)); info.name = "World"; var value2 = _httpWebRequestHelper.Request<TestWebHelperInfo>(url2, JsonConvert.SerializeObject(info)); return Ok($"value1:{value1.name} value2:{value2.name}"); } [HttpPost] public IActionResult Test1([FromBody] TestWebHelperInfo info) { return Ok(info); } [HttpPost] public IActionResult Test2([FromBody] TestWebHelperInfo info) { return Ok(info); } }
运行,然后测试一下1和2接口是否可以使用,如果可以使用,拷贝对应的url地址,当作参数传给主测试api里面。
获得到请求的url地址前缀是:http://localhost:35678/WSK/,带入参数进行验证:
由此可见,两个通用方法都可用。
备注:如果不适用泛型,也可以直接使用返回String即可,不需要进行类型转换。