这篇文章的灵感是来自XuebinDing的这篇jQuery AJAX实现调用页面后台方法和web服务定义的方法,原文讲到了这种直接调用aspx页面内的Web服务方法,非常的轻巧,但作者只给出了代码,里面一些的详细并没有给出说明,这里我会花上一些时间来去深入解析一下这其中的原理。
先来看代码,注意此方法是位于aspx代码文件中:
using System.Web.Services;
/// <summary>
/// 无参数的Ajax调用
/// </summary>
/// <returns></returns>
[WebMethod]
public static string SayHello() {
return "Hello Ajax";
}
jQuery来使用json方式调用此方法:
$.ajax({
type: 'post',
url: 'TextAjax.aspx/SayHello,
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function(data) {
alert(data.d);
}
});
效果如下:
这里需要注意以下问题:
1、WebMethod方法必须为static方法;
2、Ajax请求不能使用get方式。
3、如果使用的是VS2005,则需要在web.config中system.web段增加对System.Web.Extensions(可能需要System.Web.Extensions.dll)的引用:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
1、为何aspx能实现Web服务?
在我们的印象里貌似 asp.net的Web服务是以.asmx来结尾的,而我们现在的asp.net也能实现Web服务,这是因为默认Web.config中已经添加了System.Web.Handlers.ScriptModule,它是用于管理asp.net中ajax功能的HTTP模块,这样不管用户是请求.asmx文件还是.aspx文件,都会通过此处理程序来处理请求。
在Web.Config中可以看到对.asmx的处理程序:
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
而在vs2005(ASP.NET2.)中默认对.asmx处理的配置是这样的:
<add path="*.asmx" verb="*" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="False"/>
可见对.asmx的处理程序由System.Web.Services.Protocols.WebServiceHandlerFactory变成了System.Web.Script.Services.ScriptHandlerFactory,查看System.Web.Extensions.dll的ScriptHandlerFactory方法:
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
IHttpHandlerFactory factory;
if (RestHandlerFactory.IsRestRequest(context))
{
factory = this._restHandlerFactory;
}
else
{
factory = this._webServiceHandlerFactory;
}
IHttpHandler originalHandler = factory.GetHandler(context, requestType, url, pathTranslated);
bool flag = originalHandler is IRequiresSessionState;
if (originalHandler is IHttpAsyncHandler)
{
if (flag)
{
return new AsyncHandlerWrapperWithSession(originalHandler, factory);
}
return new AsyncHandlerWrapper(originalHandler, factory);
}
if (flag)
{
return new HandlerWrapperWithSession(originalHandler, factory);
}
return new HandlerWrapper(originalHandler, factory);
}
ScriptHandlerFactory在兼容WebServiceHandlerFactory的基础上同时增加了RestHandlerFactory的处理。
2、为何是data.d?
在js代码里你可以看到对返回对象的访问是data.d,从控制台也可以看到返回的数据是这样的:
{"d":"Hello Ajax"}
这个.d是怎么回事呢?其实它是为了避免各种XSS跨站攻击,不允许使用GET方式也是出于这个需求。详细分析可以参见这位asp.net官方人员的回复http://encosia.com/a-breaking-change-between-versions-of-aspnet-ajax/#comment-34045。
扩展
这种调用本页面的方式还是有很多可取之后,尤其是逻辑比较简单,不需要封装公共方法、只在当前页面内调用的方法来尤其合适。这里我封装了客户端调用的代码:
/*
* Ajax请求处理基础类
*/
function AjaxRequestBuiler(url, data, callback) {
var async = !(callback == undefined);
var response = $.ajax({
type: 'post',
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
async: async,
success: function(data) {
if (!!callback) {
complete(data);
}
}
});
if (!async) {
try {
return eval('(' + response.responseText + ')').d;
} catch (Error) {
}
}
function complete(obj) {
callback(obj);
}
}
包含了同步调用和异步回调的方式,使用如下:
function RequestAjax() {
var d = AjaxRequestBuiler('TestAjax.aspx/SayHello');
alert('无回调同步加载:' + d);
AjaxRequestBuiler('TestAjax.aspx/SayHello', null, function(data) {
alert('回调异步加载:' + data.d);
alert('回调成功');
});
}