代理层的主要工作是调用Web Service,将在FCL层序列化好的Json数据字符串Post到Web Service,然后获得Reponse,再从响应流中读取到调用结果Json字符串,在Dispatcher反序列化成数据对象,在UI层呈现出来。
刚开始是直接在Proxy类中直接写的Post方法,后来看到这篇帖子,将Http相关的部分封装成了工具类HttpHelper。原帖中还包含了使用TCPSocket发送请求调用WebService的内容。
1 namespace ProjectmsMGT_Proxy 2 { 3 public class HttpHelper 4 { 5 /// <summary> 6 /// Http请求URL 7 /// </summary> 8 public string Url { set; get; } 9 10 /// <summary> 11 /// 请求参数Key(约定服务方法参数名同名) 12 /// 在使用Post方式向服务器端发送请求的时候,请求数据中包含了参数部分,参数部分我们需要告诉WebService接口方法,实参要传给的接口方法行参名,由RequestParaKey指定 13 /// 当然当接口方法有多个形参时,就不建议单独设计这样一个属性,直接在sendMsg中添加,此处只是为了突出RequestParaKey的重要性 14 /// </summary> 15 public string RequestParaKey { set; get; } 16 17 /// <summary> 18 /// 证书文件路径 19 /// </summary> 20 public string CertificateFilePath { set; get; } 21 22 /// <summary> 23 /// 证书文件口令 24 /// </summary> 25 public string CertificateFilePwd { set; get; } 26 27 /// <summary> 28 /// 构造函数,不使用证书 29 /// </summary> 30 /// <param name="url"></param> 31 /// <param name="requestParaKey"></param> 32 public HttpHelper(string url, string requestParaKey) 33 { 34 this.Url = url; 35 this.RequestParaKey = requestParaKey; 36 } 37 38 /// <summary> 39 /// 构造函数,使用证书 40 /// </summary> 41 /// <param name="url"></param> 42 /// <param name="requestParaKey"></param> 43 /// <param name="certFilePath"></param> 44 /// <param name="certFilePwd"></param> 45 public HttpHelper(string url, string requestParaKey, string certFilePath, string certFilePwd) 46 { 47 this.Url = url; 48 this.RequestParaKey = requestParaKey; 49 this.CertificateFilePath = certFilePath; 50 this.CertificateFilePwd = certFilePwd; 51 } 52 53 /// <summary> 54 /// 使用Get方式,发送Http请求 55 /// </summary> 56 /// <param name="methodName">所请求的接口方法名</param> 57 /// <param name="isLoadCert">是否加载证书</param> 58 /// <returns>响应字符串</returns> 59 public string CreateHttpGet(string methodName, bool isLoadCert) 60 { 61 HttpWebRequest request = CreateHttpRequest(methodName, @"GET", isLoadCert); 62 63 return CreateHttpResponse(request); 64 } 65 66 /// <summary> 67 /// 使用Post方式,发送Http请求 68 /// </summary> 69 /// <param name="methodName">所请求的接口方法名</param> 70 /// <param name="sendMsg">请求参数(不包含RequestParaKey部分)</param> 71 /// <param name="isLoadCert">是否加载证书</param> 72 /// <returns>响应字符串</returns> 73 public string CreateHttpPost(string methodName, string sendMsg, bool isLoadCert) 74 { 75 //创建Http请求 76 HttpWebRequest request = CreateHttpRequest(methodName, @"POST", isLoadCert); 77 if (null != sendMsg && !"".Equals(sendMsg)) 78 { 79 //添加请求参数 80 AddHttpRequestParams(request, sendMsg); 81 } 82 83 //获得响应 84 return CreateHttpResponse(request); 85 } 86 87 /// <summary> 88 /// 将请求参数写入请求流 89 /// </summary> 90 /// <param name="request"></param> 91 /// <param name="sendMsg"></param> 92 private void AddHttpRequestParams(HttpWebRequest request, string sendMsg) 93 { 94 //将请求参数进行URL编码 95 string paraUrlCoded = System.Web.HttpUtility.UrlEncode(RequestParaKey) + "=" + 96 System.Web.HttpUtility.UrlEncode(sendMsg); 97 98 byte[] data = Encoding.UTF8.GetBytes(paraUrlCoded); 99 request.ContentLength = data.Length; 100 Stream requestStream = null; 101 using (requestStream = request.GetRequestStream()) 102 { 103 //将请求参数写入流 104 requestStream.Write(data, 0, data.Length); 105 } 106 107 requestStream.Close(); 108 } 109 110 /// <summary> 111 /// 创建HttpRequest 112 /// </summary> 113 /// <param name="methodName"></param> 114 /// <param name="requestType">POST或者GET</param> 115 /// <param name="isLoadCert"></param> 116 /// <returns>HttpWebRequest对象</returns> 117 private HttpWebRequest CreateHttpRequest(string methodName, string requestType, bool isLoadCert) 118 { 119 HttpWebRequest request = null; 120 try 121 { 122 string requestUriString = Url + "/" + methodName; 123 request = (HttpWebRequest)WebRequest.Create(requestUriString); 124 if (isLoadCert) 125 { 126 //创建证书 127 X509Certificate2 cert = CreateX509Certificate2(); 128 //添加证书认证 129 request.ClientCertificates.Add(cert); 130 } 131 request.KeepAlive = true; 132 request.ContentType = "application/x-www-form-urlencoded"; 133 request.Method = requestType; 134 } 135 catch (Exception) 136 { 137 //Console.WriteLine("创建HttpRequest失败。原因:" + e.Message); 138 request = null; 139 } 140 141 return request; 142 } 143 144 /// <summary> 145 /// 创建请求响应 146 /// </summary> 147 /// <param name="request"></param> 148 /// <returns>响应字符串</returns> 149 private string CreateHttpResponse(HttpWebRequest request) 150 { 151 String str; 152 HttpWebResponse response = null; 153 Stream responseStream = null; 154 XmlTextReader responseReader = null; 155 try 156 { 157 using (response = (HttpWebResponse)request.GetResponse()) 158 { 159 //获得响应流 160 responseStream = response.GetResponseStream(); 161 responseReader = new XmlTextReader(responseStream); 162 responseReader.MoveToContent(); 163 str = responseReader.ReadInnerXml(); 164 } 165 } 166 catch (Exception e) 167 { 168 str = "[{\"Rescode\":\"0\",\"Resmsg\":\"通信失败。原因:" + e.Message + "\"}]"; 169 } 170 finally 171 { 172 if (null != response) 173 { 174 responseReader.Close(); 175 responseStream.Close(); 176 response.Close(); 177 } 178 } 179 180 return str; 181 } 182 183 /// <summary> 184 /// 创建证书 185 /// </summary> 186 /// <returns>X509Certificate2对象</returns> 187 private X509Certificate2 CreateX509Certificate2() 188 { 189 X509Certificate2 cert = null; 190 try 191 { 192 cert = new X509Certificate2(CertificateFilePath, CertificateFilePwd); 193 ServicePointManager.ServerCertificateValidationCallback = 194 new RemoteCertificateValidationCallback(ServerCertificateValidationCallback); 195 } 196 catch (Exception) 197 { 198 //Console.WriteLine("创建X509Certificate2失败。原因:" + e.Message); 199 cert = null; 200 } 201 return cert; 202 } 203 204 /// <summary> 205 /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication 206 /// </summary> 207 /// <param name="obj">An object that contains state information for this validation</param> 208 /// <param name="cer">The certificate used to authenticate the remote party</param> 209 /// <param name="chain">The chain of certificate authorities associated with the remote certificate</param> 210 /// <param name="error">One or more errors associated with the remote certificate</param> 211 /// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication</returns> 212 private bool ServerCertificateValidationCallback(object obj, X509Certificate cer, X509Chain chain, System.Net.Security.SslPolicyErrors error) 213 { 214 return true; 215 } 216 } 217 }
HttpHelper中把SSL证书的部分也包含进来,但是证书认证机制部分ServerCertificateValidationCallback还没设计,各位大神可以自行发挥。
有了HttpHelper之后,代理类的代码就比较明了了。
1 namespace ProjectmsMGT_Proxy 2 { 3 public class ProjectmsProxy 4 { 5 private readonly string Url = "http://59.68.29.106:8087/IFT_Project.asmx";//通过配置文件获取Web Service地址 6 private readonly string requestParaKey = "paramaters";//服务端所有接口函数统一的参数名 7 private HttpHelper httpHelper; 8 9 public ProjectmsProxy() 10 { 11 //初始化 12 Initialize(); 13 } 14 15 private void Initialize() 16 { 17 httpHelper = new HttpHelper(this.Url, this.requestParaKey); 18 } 19 20 /// <summary> 21 /// 使用Get方式调用WebService,不带参数 22 /// </summary> 23 /// <param name="methodName"></param> 24 /// <param name="parasJsonStr"></param> 25 /// <param name="requestType"></param> 26 /// <returns></returns> 27 public string Excute(string methodName, string parasJsonStr, string requestType) 28 { 29 return httpHelper.CreateHttpGet(methodName, false); 30 } 31 32 /// <summary> 33 /// 默认使用Post方式调用WebService,带参数 34 /// </summary> 35 /// <param name="methodName"></param> 36 /// <param name="parasJsonStr"></param> 37 /// <returns></returns> 38 public string Excute(string methodName, string parasJsonStr) 39 { 40 return httpHelper.CreateHttpPost(methodName, parasJsonStr, false); 41 } 42 43 /// <summary> 44 /// 默认使用Post方式调用WebService,不带参数 45 /// </summary> 46 /// <param name="methodName"></param> 47 /// <returns></returns> 48 public string Excute(string methodName) 49 { 50 return httpHelper.CreateHttpPost(methodName, null, false); 51 } 52 } 53 }
Proxy中重载了Excute方法,三个参数的表示使用Get方式调用WebService(因为不建议在Get方式下传参给Web Service),两个参数和一个参数的Excute默认是使用Post方式带参数和不带参数的情况。
将方法名作为参数Post到Web Service可以减少很多重复代码,不需要对服务端的每个接口函数做写一个代理函数,这是使用Post方式比使用添加Web服务引用方式更加灵活。