使用WPF来做项目时,服务器端用 web api后,自然得面对着怎样调用web api的问题,如果没有良好的将访问web api功能进行封装的话,可能会写很多的冗余代码;在刚开始搭建这个框架的时候,也在考虑这个问题,当时还想着将祖传的访问web api的代码翻出来用用,结果还是做罢,一是懒得翻了,二来是代码也不算简洁,因此就懒得拿出来丢人现眼了。

WPF调用Web API
如果没有将访问web api做良好封装的话,WPF调用Web API基本上代码写起来是非常的不舒服的,就像下面冗余的调用方式一样,参数传得多是其一,其二是处理方式拿到项目中来就有点非正式场合使用了,尽管可以实现功能,当然如果您对代码要求不高的话,其实,随便咋整都行。
冗余的调用方式
WPF想要调用web api,经常可以看到的代码会是这样写的:
public T CallWebAPi<T>(string userName, string password, Uri url, out bool isSuccessStatusCode)
{
T result = default(T);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = url;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
HttpResponseMessage response = client.GetAsync(url).Result;
isSuccessStatusCode = response.IsSuccessStatusCode;
var JavaScriptSerializer = new JavaScriptSerializer();
if (isSuccessStatusCode)
{
var dataobj = response.Content.ReadAsStringAsync();
result = JavaScriptSerializer.Deserialize<T>(dataobj.Result);
}
else if (Convert.ToString(response.StatusCode) != "InternalServerError")
{
result = JavaScriptSerializer.Deserialize<T>("{ \"APIMessage\":\"" + response.ReasonPhrase + "\" }");
}
else
{
result = JavaScriptSerializer.Deserialize<T>("{ \"APIMessage\":\"InternalServerError\" }");
}
}
return result;
}
代码来源:https://stackoverflow.com/questions/18631091/how-to-call-web-api-in-wpf-4-0
这里也给大伙展示一下早前项目中的访问web api是怎样的一种方式。
public static class HttpHelper
{
public static string BaseURI{get;set;}
public static string SessionId { get; set; }
private static readonly Encoding Encoding = Encoding.GetEncoding("UTF-8");
private static readonly int Timeout = 100000;
private static readonly string ContentTypePost = "application/json";
private static readonly string ContentTypePost2 = "application/x-www-form-urlencoded";
/// <summary>
/// 調用post請求接口
/// </summary>
/// <param name="apiName">接口名稱</param>
/// <param name="data">參數格式(JOSN)</param>
/// <param name="isParameter"></param>
/// <returns></returns>
async public static Task<string> PostAsyc(string apiName, string data, bool isParameter = false)
{
return await Task.Run(() =>Send(apiName, data, isParameter));
}
/// <summary>
///調用post請求接口
/// </summary>
/// <param name="apiName">接口名稱</param>
/// <param name="data">參數格式</param>
/// <param name="isParameter"></param>
/// <returns></returns>
public static string Post(string apiName, string data, bool isParameter = false)
{
return Send(apiName, data, isParameter);
}
private static string Send(string apiName, string data, bool isParameter)
{
string result = null;
try
{
using (HttpWebResponse response = GetResponse(apiName, data, isParameter))
{
if (string.IsNullOrEmpty(SessionId))
{
SessionId = response.Headers.Get("Set-Cookie");
}
Stream stream = response.GetResponseStream();
if (stream != null)
{
StreamReader reader = new StreamReader(stream, Encoding);
result = reader.ReadToEnd();
}
}
return result;
}
catch (Exception e)
{
var ret = new ResultJsonBase() { Succeeded = false, Message = e.Message, Data= "[]" };
return JsonHelper.SerializeObject(ret);
}
}
private static HttpWebResponse GetResponse(string apiName, string data, bool isParameter)
{
StringBuilder baseURL = new StringBuilder(BaseURI);
baseURL.Append(apiName);
string contentType = ContentTypePost;
if (isParameter)
{
contentType = ContentTypePost2;
if (!string.IsNullOrWhiteSpace(data))
{
data = data.ToLower();
JObject urlParam = (JObject)JsonConvert.DeserializeObject(data);
baseURL.Append("?");
var listParam = urlParam.Properties().Select(p =>
{
return string.Format("{0}={1}", p.Name, p.Value);
});
string param = String.Join("&", listParam);
baseURL.Append(param);
}
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(baseURL.ToString());
request.Method = "POST";
request.UserAgent = "Mozilla/5.0";
request.Timeout = Timeout;
request.ContentType = contentType;
request.ContentLength = 0;
object accessToken = MemoryCacheManager.Get(UserContext.AccessToken);
if (accessToken != null)
{
//token格式,Header裏面設置
request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken.ToString()));
}
if (!string.IsNullOrEmpty(SessionId))
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.SetCookies(new Uri(baseURL.ToString()), SessionId);
}
if (!isParameter)
{
if (!string.IsNullOrEmpty(data))
{
byte[] bytes = Encoding.GetBytes(data);
request.ContentLength = bytes.Length;
request.GetRequestStream().Write(bytes, 0, bytes.Length);
}
else
{
request.ContentLength = 0;
}
}
try
{
return (HttpWebResponse)request.GetResponse();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 測試服務器是否處於連接狀態
/// </summary>
/// <param name="endPoint">端口號</param>
/// <param name="ip">ip地址</param>
async public static Task<bool> ConnectTest(string ip, int endPoint)
{
var client = new HttpClient();
client.BaseAddress = new Uri($"http://{ip}:{endPoint}");
client.Timeout = TimeSpan.FromSeconds(5);
try
{
var response = await client.PostAsync("/api/Login/TestConnect", null);
return response.StatusCode == HttpStatusCode.OK;
}
catch
{
return false;
}
}
/// <summary>
/// 通過post方式調用通用上傳文件接口
/// </summary>
/// <param name="apiName">接口名稱</param>
/// <param name="para">參數格式(json格式)</param>
/// <param name="fileNames">文件路徑</param>
/// <returns></returns>
public async static Task<string> PostFilesAsync(string apiName, string para, List<string> fileNames)
{
if (fileNames != null && fileNames.Count > 0)
{
var baseURL = new StringBuilder();
baseURL.Append(BaseURI);
baseURL.Append(apiName);
if (!string.IsNullOrWhiteSpace(para))
{
JObject urlParam = (JObject)JsonConvert.DeserializeObject(para);
baseURL.Append("?");
var listParam = urlParam.Properties().Select(p =>
{
return string.Format("{0}={1}", p.Name, p.Value);
});
string param = String.Join("&", listParam);
baseURL.Append(param);
}
var client = new RestClient(baseURL.ToString());
client.Timeout = -1;
var request = new RestRequest(Method.POST);
//設置token和添加上傳文件參數
object accessToken = MemoryCacheManager.Get(UserContext.AccessToken);
if (accessToken != null)
request.AddHeader("Authorization", string.Format("Bearer {0}", accessToken.ToString()));
foreach (var item in fileNames)
request.AddFile("files", item);
IRestResponse response = await client.ExecuteAsync(request);
return response.Content;
}
return null;
}
}
以上代码呢,可能就是常见的一种封装了,简单的弄个 XXXHelper就行了,而在需要调用的地方呢,还得这样写才行:
string jsonResult = HttpHelper.Post(url, strQuery, true); var Result = JsonHelper.AnalyzeJsonString(jsonResult);
上面的url参数,是web api接口的URL,是这样定义的:
public class ApiNames
{
/// <summary>
/// 通用上传附件接口
/// </summary>
public const string FileUpload = "/api/Common/Upload";
/// <summary>
/// 获取病人信息
/// </summary>
public const string GetPatientInfo = "/api/Common/GetPatientInfo";
}
这可能是我们都会这样干的一种方式来编写web api接口的方式,上面的方式还得处理收到响应内容再反序列化成需要的对象,相信经常写这种代码的猿呢,是非常苦恼的。
早点下班更好写代码的方式
程序猿,早点下班何乐而不为呢?更多的时间做自己想做的事情,如建个自己的网站来赚取被动收入不是更好吗?
在JHRS框架中,WPF访问web api是引入了refit来解决冗余代码和优雅的问题,因此在实际项目中,您要做的事情就2个。
第一个:编写与web api对应的接口
public interface IGitHubApi
{
[Get("/users/{user}")]
Task<User> GetUser(string user);
}
来自github refit示例接口
第二个:调用它完成你的目的
var gitHubApi = RestService.For<IGitHubApi>("https://jhrs.com");
var octocat = await gitHubApi.GetUser("octocat");
从上面的例子看出,WPF调用Web API是不是很简单了呢?但第一个编写web api接口这个事情,在大型项目中,接口成百上千个,手工编写也是一个体力活;因此还得想些招来减少干这些体力活,因此在框架中,顺手编写了一个反向解析swagger的工具,尽管可能存在bug,但修复起来也不难,因为这个工具在我们的项目中,已经被团队成员使用了。

是不是很省事了呢?有些体力活就该交给工具来给你完成。
写在最后
本篇介绍完了在真实项目中经常会写的高频代码WPF调用Web API的封装理念,以及我的一些懒人方法,最终的目的说的高大上呢就是为了提高工作效率,说的实在点呢,就是为了早点下班,不要996这种生活。
下一篇会介绍客户端是怎样设计入口项目的,即介绍一下JHRS.Shell这个入口项目库里面的代码上的一些事情,以及为什么这样做的。
本系列相关阅读
- WPF企业级开发框架搭建指南(启示录)
- JHRS开发框架之基础类库
- JHRS开发框架之第三方框架选型
- JHRS开发框架之WPF调用Web API封装
- JHRS开发框架之客户端入口项目
- JHRS开发框架之各子系统如何整合
- JHRS开发框架之怎样设计合理的ViewModel基类
- JHRS开发框架之公用组件用户控件的封装
- JHRS开发框架之建议遵循的一些建目录文件原则
- JHRS开发框架之WPF数据验证
- JHRS开发框架之ViewModel相互传参和弹框回传参的解决办法
- JHRS开发框架之踩坑记(终章)
【江湖人士】(jhrs.com) 投稿作者:IT菜鸟,不代表江湖人士立场,如若转载,请注明出处:https://jhrs.com/2020/38038.html
扫码加入电报群,让你获得国外网赚一手信息。