版权声明:原创作品,如需转载,请与作者联系。否则将追究法律责任。 |
在上一篇文章里,我们分析了一部分服务器端的代码。到现在为止,我们已经获得处理Web Services方法请求的Handler,马上就要开始Process Request了。
我们知道,处理Web Services方法请求的Handler是RestHandler,所以我们来看一下它的ProcessHandler方法:
 ProcessRequest获得调用结果代码分析 1 public void ProcessRequest(HttpContext context) 2 { 3 // 根据Web Services的Cache设置来配置Cache策略 4 this.InitializeCachePolicy(context); 5 6 try 7 { 8 string contentType; 9 10 // 从body的JSON字符串中得到输入的参数。 11 IDictionary<string, object> inputParams = this.GetRawParams(context); 12 13 // this._webServiceMethodData.Ower.Type即获得了即将调用的那个 14 // Web Service类,通过Activator.CreateInstance方法构造实例。 15 object serviceObj = Activator.CreateInstance(this._webServiceMethodData.Owner.Type); 16 17 // 这是一段很有技巧的代码,我们目前不去分析它。 18 DataService service = serviceObj as DataService; 19 if (service != null) 20 { 21 service.IsCalledRemotely = true; 22 } 23 24 // Call这个Web Service方法来得到结果 25 object resultObj = this._webServiceMethodData.CallMethodFromRawParams(serviceObj, inputParams); 26 27 …… 28 } 29 catch (Exception e) 30 { 31 …… 32 } 33 }
首先调用InitializeCachePolicy方法来处理缓存策略,代码很短也很简单,因此不多解释了。其次查看serviceObj
是否是DataService类型,如果是的话则将IsCalledRemotely设为ture,这是比较有技巧的做法,我们目前不去分析它。接着通过
GetRawParams方法方法获得以Dictionary方式存放的参数,我们需要看一下它的框架,可以了解它获得参数的方法。
 GetRawParams(HttpContext) 1 private IDictionary<string, object> GetRawParams(HttpContext context) 2 { 3 // 如果是Cross Domain Access,则抛出异常 4 if (!this._webServiceMethodData.SafeForCrossDomain && ChildRequest.IsCrossDomainRequest(context)) 5 { 6 throw new InvalidOperationException( 7 string.Format( 8 CultureInfo.CurrentCulture, 9 AtlasWeb.InvalidCrossDomainRequest, 10 new object[] { this._webServiceMethodData.MethodInfo.Name })); 11 } 12 13 // 如果使用HTTP POST方法 14 if (context.Request.HttpMethod == "POST") 15 { 16 // 则通过Body中的JSON代码获得参数 17 return this.GetRawParamsFromPostRequest(context); 18 } 19 20 // 由于使用了HTTP GET方法,需要看一下Web Services方法是否允许GET操作 21 if (!this._webServiceMethodData.GetVerbEnabled) 22 { 23 throw new InvalidOperationException( 24 string.Format( 25 CultureInfo.CurrentCulture, 26 AtlasWeb.InvalidGetRequest, 27 new object[] { this._webServiceMethodData.MethodInfo.Name })); 28 } 29 30 // 从Query String中获得参数 31 return this.GetRawParamsFromGetRequest(context); 32 }
一个Web Service方法,可以使用Microsoft.Web.Services.WebOperationAttribute来标记是否使用能够通过GET方法访问。下面的代码让该Web Service方法允许使用GET方法来访问:
[WebOperation(true)]
获得的Dictionary数据结构以Key -
Value的方式对应的参数名和表示参数值的字符串,如果是复杂类型的话会产生许多层的Dictionary或List,大家应该能够想象出来它是什么样
子,因为这和大名鼎鼎的JSON非常相似!获得参数之后,会将其传入WebServiceMethodData的
CallMethodFromRawParams方法,以获得方法执行的结果。
WebServiceMethodData.CallMethodFromRawParams方法代码如下:
 CallMethodFromRawParams方法分析 1 internal object CallMethodFromRawParams(object target, IDictionary<string, object> parameters) 2 { 3 // 获得强类型的参数字典 4 parameters = this.StrongTypeParameters(parameters); 5 return this.CallMethod(target, parameters); 6 }
首先通过StrongTypeParameters方法来获得一个强类型的参数字典,它不会有多层的Dictionary或List。此方法
非常的复杂,在这里就先不进行分析了,有兴趣的朋友可以先看一下相关代码,我会在今后的文章中分析这些代码的细节,它们还是写得非常优秀的。得到强类型的
参数后,就会使用CallMethod来调用方法得到结果了。在这里面只是使用了简单的Reflection,相信大家也能够想象得到个中实现。
接下来就要输出结果了,代码如下:
 ProcessRequest输出结果代码分析 1 public void ProcessRequest(HttpContext context) 2 { 3 …… 4 5 try 6 { 7 …… 8 9 // 如果这个Web Service方法被要求使用XML作为Response 10 if (this._webServiceMethodData.UseXmlResponse) 11 { 12 // 如果result是String,那么直接输出 13 if (resultObj is string) 14 { 15 body = (string) resultObj; 16 } 17 else if (resultObj is XmlNode) 18 { 19 // 如果是一段XML,则输出它的OuterXml 20 body = ((XmlNode) resultObj).OuterXml; 21 } 22 else // 如果只是一个普通的对象 23 { 24 try 25 { 26 // 使用XmlSerializer来序列化对象 27 body = ServicesUtilities.XmlSerializeObjectToString(resultObj); 28 } 29 catch (Exception e) 30 { 31 throw new InvalidOperationException( 32 string.Format( 33 CultureInfo.CurrentCulture, 34 AtlasWeb.InvalidXmlReturnType, 35 new object[] { 36 this._webServiceMethodData.MethodInfo.Name, 37 resultObj.GetType().FullName, 38 e.Message 39 } 40 ) 41 ); 42 } 43 } 44 45 // contentType为"text/xml" 46 contentType = "text/xml"; 47 } 48 else // 如果不以Xml输出 49 { 50 // 那么以JSON方式输出 51 body = JavaScriptObjectSerializer.Serialize(resultObj, this._webServiceMethodData.Owner); 52 // contentType为"application/json" 53 contentType = "application/json"; 54 } 55 56 // 设置ContentType 57 context.Response.ContentType = contentType; 58 // 输出body 59 if (body != null) 60 { 61 context.Response.Write(body); 62 } 63 } 64 catch (Exception e) 65 { 66 …… 67 } 68 }
要设置该Web Services方法的输出方式为XML(UseXmlResponse == true),则也是需要使用Microsoft.Web.Services.WebOperationAttribute来标记方法,如下:
[WebOperation(false, ResponseFormatMode.Xml)]
后面会有一个例子来演示这一点。如果该方法被标记使用XML方式输出,则会判断结果类型。如果是字符串则直接输出;如果是Xml类型的结果,则输出它的OuterXml;最后则会尝试使用XmlSerializer来序列化这个对象。
在默认情况下,该对象会被JSON序列化输出,这就是我们最常见的做法。
最后,对于异常情况,也需要进行输出。代码如下:
 ProcessRequest输出异常代码分析 1 public void ProcessRequest(HttpContext context) 2 { 3 …… 4 5 try 6 { 7 …… 8 } 9 catch (Exception e) 10 { 11 // 输出异常信息 12 context.Response.ClearHeaders(); 13 context.Response.Clear(); 14 // Status Code设为500 15 context.Response.StatusCode = 500; 16 context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(500); 17 using (StreamWriter writer = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false))) 18 { 19 // 以JSON方式输出异常信息 20 RestHandler.WriteExceptionJsonString(context, writer, e); 21 return; 22 } 23 } 24 }
代码使用RestHandler.WriteExceptionJsonString来分别输出异常的Message,StackTrace和异常的FullName。在代码里可以使用这一点。
到现在为止,可以说Atlas在服务器端对于Web Services的支持代码已经分析完了。我们通过两个实例来详细理解这一点。
范例1:在Web Services方法中使用复杂的数据类型。
首先,我们定义两个表示数据的类,Employee和Company。代码如下:
 Employee与Company代码 1 [Serializable] 2 public class Employee : IComparable<Employee> 3 { 4 public string Name; 5 6 public int Age; 7 8 #region IComparable<Employee> Members 9 10 public int CompareTo(Employee other) 11 { 12 return this.Name.CompareTo(other.Name); 13 } 14 15 #endregion 16 } 17 18 [Serializable] 19 public class Company 20 { 21 public string Name; 22 23 public Employee[] Employees; 24 }
接着我们定义一个Web Services方法Sort,该方法的作用是拿到公司姓名和一个Employee数组作为参数,将Employee按照姓名排序之后,再组成一个Company对象输出。代码如下:
 Sort方法 1 [WebService(Namespace = "http://tempuri.org/")] 2 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 3 public class ComplexTypeWS : System.Web.Services.WebService { 4 5 [WebMethod] 6 public Company Sort(string companyName, Employee[] employees) 7 { 8 Array.Sort<Employee>(employees); 9 10 Company company = new Company(); 11 company.Name = companyName; 12 company.Employees = employees; 13 return company; 14 } 15 }
然后就是HTML了。在页面最上方(id为employees的div)会显示内存中目前所有的Employee,之后是向内存中添加
Employee的输入框,接着是填写公司名的文本框和排序按钮,最后则是经过了Web
Services排序后的结果显示区域(id为sortedDisplay的div):
 HTML代码 1 <body style="font-family:Verdana; font-size: 14px;"> 2 <form id="form1" runat="server"> 3 <atlas:ScriptManager runat="server" ID="ScriptManager1" /> 4 5 <div>Employees:</div> 6 <div id="employees"></div> 7 <hr /> 8 <div>Add Employee:</div> 9 <div>Name: <input type="text" id="empName" /></div> 10 <div>Age: <input type="text" id="empAge" /></div> 11 <input type="button" value="Add employee" onclick="addEmployee()" /><br /> 12 <hr /> 13 <div>Company Name:<input type="text" id="companyName" /></div> 14 <input type="button" value="Sort!" onclick="sort()" /><br /> 15 <hr /> 16 <div id="sortedDisplay"></div> 17 </form> 18 </body>
最后我们来看Javascript代码:
 Javascript代码 1 <script language="javascript"> 2 // 内存中的Employee数组 3 var empArray = new Array(); 4 5 // 添加一个Employee 6 function addEmployee() 7 { 8 // 建立一个对象表示Employee 9 var emp = new Object(); 10 emp.Name = $('empName').value; 11 emp.Age = parseInt($("empAge").value, 10); 12 13 // 加入数组 14 empArray.push(emp); 15 16 // 更新最上方的显示 17 updateSource(); 18 } 19 20 // 将内存中的empArray数组显示在id为employee的div中 21 function updateSource() 22 { 23 var html = ""; 24 25 for (var i = 0; i < empArray.length; i++) 26 { 27 var emp = empArray[i]; 28 html += ((i + 1) + ". " + emp.Name + ", " + emp.Age + " years old.<br />") 29 } 30 31 $("employees").innerHTML = html; 32 } 33 34 // 访问Web Service进行排序 35 function sort() 36 { 37 // 构造参数 38 var params = { "companyName" : $("companyName").value, "employees" : empArray }; 39 // 构造Web Service方法访问对象 40 var method = new Sys.Net.ServiceMethod("ComplexTypeWS.asmx", "Sort", null); 41 42 // 调用Web Service方法 43 method.invoke(params, onMethodComplete); 44 } 45 46 // 回调函数 47 function onMethodComplete(company, response, userContext) 48 { 49 // 在id为sortedDisplay的div中显示所有的Employee, 50 // 可以发现company对象和服务器端对象的结构相同 51 var html = "Company Name: " + company.Name; 52 for (var i = 0; i < company.Employees.length; i++) 53 { 54 var emp = company.Employees[i]; 55 html += ("<br />" + (i + 1) + ". " + emp.Name + ", " + emp.Age + " years old.") 56 } 57 58 $("sortedDisplay").innerHTML = html; 59 60 // 清空内存中的Employee 61 empArray.length = 0; 62 // 更新最上方的显示 63 updateSource(); 64 } 65 </script>
所有的代码都在这里,我们来看一下使用。首先打开页面,输入数个Employee,如图:

然后点击填写好Company Name并点击Sort按钮,则可以看出按照姓名排序后的结果:

我们使用Fiddler查看一下数据传输,可以看到Request Body和Response Body里的JSON代码:

可以看出,Atlas使用了JSON方式传递数据非常的直观,对于复杂的类型支持也非常好。在客户端得到的对象,其结构和服务器端相同,这对于开发人员带来了不小的便利。
范例2:使用Web Services将对象序列化成XML并使用客户端XSLTView空间输出信息
使用了与上例相同的Employee和Company两个类,在这里就不重复了,先来看一下Web Service方法GetXmlSerializedCompany的代码:
 GetXmlSerializedCompany方法代码 1 [WebService(Namespace = "http://tempuri.org/")] 2 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 3 public class ComplexTypeWS : System.Web.Services.WebService { 4 5 [WebMethod] 6 [WebOperation(false, ResponseFormatMode.Xml)] 7 public Company GetXmlSerializedCompany(Company company) 8 { 9 return company; 10 &nb |