Introduction
I will share my Ajax solution by Jquery with JSON format in the next two parts. In the first part, I will describe how to implement Jquery Ajax call in the ASP.NET page.
In the second part, I will describe my Ajax datagrid
user control that implements Jquery and Ajax call with JSON format.
Background
The reason that we don't use Microsoft Ajax library is that we try to avoid the transparent code which is provided by Microsoft and also get flexibility to handle code and logic by ourselves, so that we can keep the code lighter and easier to manage and high performance, on the other hand, we will use Jquery library, because it has become popular and more like a standard.
You might see similar solutions somewhere else, I combined them together with my idea and make them work.
Summary
In short, the Ajax route is client fire Ajax call request, the request includes the function name that will be called in server side and the parameters and the callback function name, then the server processes the request and returns string
result back to client. The result is serialized as JSON format string
if it is not value type.
Step by Step
To make the route clear, I will demo it step by step:
Step 1: Reference Jquery
As I mentioned, we will implement Jquery library and its Ajax call. then we need load Jquery libraries on our pages first.
To import the Jquery, you can reference it in your master page or your page header or a header user control.
<HEAD runat="server">
......your other code......
<script type="text/javascript"
src="https://Ajax.googleapis.com/Ajax/libs/Jquery/1.6.1/Jquery.min.js">
</script>
<script type="text/javascript"
src="https://Ajax.googleapis.com/Ajax/libs/Jqueryui/1.8.13/Jquery-ui.min.js">
</script>
<script type="text/javascript">
$(document).ready(function() {
setTimeout ( "try{pageReady();}catch(e){}", 0 );
......your other code......
});
......your other code......
</script>
</HEAD>
Since our project was started from ASP.NET 1.1, we reference it in a pageHeader
user control like the above. We reference it to Google site, then client won't bother our server to transfer the file even it is tiny. If you use Jquery, you should know $(document).ready
function that will be called after all DOM objects were established in the page. We forward this call to page level, to call pageReady()
that locates in our specify page and let it prepare data for each page. Due to this, pageReady()
is optional and not every page needs it, so we put it in a try
block.
Another point that we might pay attention to is the DOCTYPE
in the page, it must be a new standard that supports xhtml to allow you use some Jquery function.
It might be other versions, but this works in my site.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Step 2: Build Ajax page (Jpage)
In this step, built the Ajax page (Jpage) without UI that allows other pages to inherit it. Its purpose is to load our Ajax function in the page and initialize the basic data for it, it also handles all the Ajax requests and invokes the method in your page by reflection.
This page is similar with Microsoft Ajax scriptManager
. The code is shown below (read the common for statement purposes):
using System;
using System.Text;
using System.Reflection;
using System.Web.Script.Serialization;
using System.Web;
namespace YourLogicNameSpace.YourDedefineName
{
public abstract class Jpage : System.Web.UI.Page
{
#region Variables
protected static JavaScriptSerializer serializer = new JavaScriptSerializer();
#endregion
#region Web Form Designer generated code
.........
#endregion
#region Properties
public string PathToJpageScript {
get { return Page.ResolveUrl("YourPath/Jpage.js") +
"?version=" + Config.Version; }
}
#endregion
#region Events
private void Page_PreLoad(object sender, System.EventArgs e) {
HandleCallbacks();
}
private void Page_Load(object sender, System.EventArgs e) {
StringBuilder sb = new StringBuilder();
sb.Append("var Jpage={};");
sb.Append(string.Format("Jpage.msgExpired='{0}';",
Global.GetRes("Resources.Session.Expired")));
sb.Append(string.Format("Jpage.loginLink='{0}';",
this.Request.ApplicationPath + "/Login.aspx?ReturnUrl=" +
Global.GetEscapedUrl(this.Request.RawUrl)));
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"Jpage_Block", sb.ToString(), true);
Page.ClientScript.RegisterClientScriptInclude(this.GetType(),
"Jpage", PathToJpageScript);
}
#endregion
#region Ajax Methods
protected void HandleCallbacks() {
string callMethod = Request.Params["Ajax"];
if (string.IsNullOrEmpty(callMethod))
return;
try {
var result = this.GetType().GetMethod
(callMethod, BindingFlags.NonPublic |
BindingFlags.Instance).Invoke(this, null);
AjaxResponse((result is string || result.GetType().IsValueType)?
result.ToString():serializer.Serialize(result));
} catch (Exception) {
Response.StatusCode = 500;
Response.Write("Server callback exception");
Response.End();
}
}
protected void AjaxResponse(string result) {
Response.Clear();
Response.ContentType = "application/json";
Response.Write(result);
Response.Flush();
Response.End();
}
#endregion
#region Helper Methods
.....some utility methods that you want, like parse query string to parameter .....
#endregion
}
}
When we make an Ajax call by Jquery in the page, it passes the method name that we want to invoke in query string name [Ajax], then in HandleCallbacks
methods, we check whether it is an Ajax call. If it is, we use reflection and try to invoke the method, the methods will return a result that could be a value type, string or object. If the result is object, we serialize the result to string as JSON format and call AjaxResponse()
to return back to client.
Step 3: Build a Tiny JS File (Less than 1K)
For Ajax page to take care of Ajax request, response, error and session timeout, all Jpage properties are initialized in the C# script block.
var pgUrl=document.location.href;
var loginPattern=/<\s*html|HTML\s*>\s*<\s*head|HEAD\s*>.
*<\s*\/\s*head|HEAD\s*>\s*<\s*body|BODY\s*>.*<\s*form|FORM.*
(YOUR LOGIN PAGE PATH .*>.*/m;
function AjaxPath(){return (pgUrl+((-1<pgUrl.indexOf('?'))?'&':'?')+
'Rand='+new Date().getTime()+'&Ajax=');}
function callAjax(funcName,Param,funcCallBack,fSync){
if(fSync){var V=null;}
$.AjaxSetup(
{error:function(xhr,status,err){handleAjaxError(xhr,status,err,funcName+' error.');},
type:'POST',
timeout:100000,
async:(!fSync||fSync==null)
});
$.getJSON(
AjaxPath()+funcName,
Param,
function(result,status,xhr){
if(result===false){testSession(JSON.stringify(xhr.responseText));}
if(fSync){V=result;}
else{window[funcCallBack](result);}
});
if(fSync){return V};
}
function handleAjaxError(xhr,status,err,errMsg){
if(xhr.readyState==0||xhr.status==0){return;}
else{alert(xhr.responseText+'; '+errMsg);}
}
function testSession(ResTxt){
try{
if(loginPattern.test(ResTxt.substring(0,10000))){
alert(Jpage.msgExpired);
window.location=Jpage.loginLink;
}
}catch(x){}
}
In the file, the callAjax()
method fires Jquery Ajax call and handles the response, Ajax error and session timeout.
The function has 4 arguments, the function name that we want to call in the server, the parameter object that carries all the parameters, the callback function name that will handle the response from the server. The last one is the flag to allow you to call Ajax synchronize.
In the Jquery $.AjaxSetup
:
handleAjaxError()
function will display the error message from the server and also the invoke method name. To set the Ajax call type to 'POST
' can make it accept big size query string and handle URL encoding.
async
flag allows to make a synchronized Ajax call.
In the Jquery $.getJSON
:
Initialize the Ajax URL as current page location and attach the method name that you want to invoke in server side, notice the "Rand=
" in AjaxPath
method, it uses current timestamp to prevent browser caching your call. When the Ajax call succeeds, first we check the result:
if(result===false){testSession(JSON.stringify(xhr.responseText));}
In our project, if session is out, the server will redirect you to the login page. The content will be sent as responseText
, but the result always is false
, so it checks if result is false
first, then test its text whether it is login page and redirect to login page in client side, otherwise it passes the result through to the next step.
After test session, it checks whether it is synchronize call, if yes, returns the result, otherwise calls the callback function by name passed with the result.
Step 4: Implement in the Page
First inherit the Jpage
:
public class Locations : Jpage {
......
}
Create the methods that you want to call in Ajax that are just like a normal method, but make sure these methods must be protected and have a return value. the reason why it needs to be protected is the reflection has restriction which set the flags "BindingFlags.NonPublic | BindingFlags.Instance
" for the methods that it invokes, so that we can implement the encapsulation. Because we need to return the data to the client, it must have a return value. On the other hands, the return result can be different types. If it is string
or valueType
, it will be converted to string
, otherwise, it will be serialized to JSON format as string
to return.
In these methods, they will be invoked from Jpage HandleCallbacks()
by reflection, sample shown below (I will discuss more details inside this code in the next part):
#region Ajax methods
protected object getList() {
var result = YourLogicLayer.LookupLoation(Param("Company"),
Param("Loc"), Param("Zip"), Param("Phone"));
var list = result.OfType<YourLogicClass>().Select(i => new {
i.ID,
i.Name,
i.Company, Address=i.Address.FullAddressText
}).ToList().ToDataTable().ToJsonString();
return new { total = result.VirtualRows, list };
}
protected int delRow() {
return YourLogicLayer.Delete(Convert.ToInt32(ParamId("Id")));
}
#endregion
In the page JavaScript file, make the Ajax call by calling callAjax()
which loaded in Jpage and pass method name in code behind, parameter and callback function name. If the return value is JSON format, you can directly access the result property to get what you want. The code is like this:
function tbResourceDelete(r){
if(confirmDelete(r)){
callAjax('delRow',{Id:r.find('td:eq('+tdIdx('IdCol')+')').html()},
'tbResourceDeleteCallBack');
}
}
function tbResourceDeleteCallBack(result){
if(1==result){goPage(currentPg);}
else{alert('delete failure.')}
}
In the next part, I will demo an Ajax datagrid
user control using Jquery and JSON format and this Jpage
function.