站点图标 江湖人士

JHRS开发框架之WPF调用Web API封装

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

JHRS开发框架之WPF调用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,但修复起来也不难,因为这个工具在我们的项目中,已经被团队成员使用了。

JHRS开发框架之WPF调用Web API封装

是不是很省事了呢?有些体力活就该交给工具来给你完成。

写在最后

本篇介绍完了在真实项目中经常会写的高频代码WPF调用Web API的封装理念,以及我的一些懒人方法,最终的目的说的高大上呢就是为了提高工作效率,说的实在点呢,就是为了早点下班,不要996这种生活。

下一篇会介绍客户端是怎样设计入口项目的,即介绍一下JHRS.Shell这个入口项目库里面的代码上的一些事情,以及为什么这样做的。

本系列相关阅读

  1. WPF企业级开发框架搭建指南(启示录)
  2. JHRS开发框架之基础类库
  3. JHRS开发框架之第三方框架选型
  4. JHRS开发框架之WPF调用Web API封装
  5. JHRS开发框架之客户端入口项目
  6. JHRS开发框架之各子系统如何整合
  7. JHRS开发框架之怎样设计合理的ViewModel基类
  8. JHRS开发框架之公用组件用户控件的封装
  9. JHRS开发框架之建议遵循的一些建目录文件原则
  10. JHRS开发框架之WPF数据验证
  11. JHRS开发框架之ViewModel相互传参和弹框回传参的解决办法
  12. JHRS开发框架之踩坑记(终章)
退出移动版