views:

332

answers:

5

Hi, I want to write my own AJAX in ASP.NET, and not use the ASP.NET ScriptManager, etc.

WHY? I like doing stuff manually and knowing how stuff works from the inside, so I just want to do it for myself.

So my question is, after I make an AJAX call:

var ajaxCall = new XMLHttpRequest();
....
ajaxCall.send(null)

How can I, in C#, add in the Page_Load (or not) so that it listens for this and returns a String for example.

+2  A: 

+1 for you doing things yourself - I like to know that I can do things myself before using frameworks that do it for me, so if it goes tits up, I roughly know how to begin fixing it.

Anyway down to your question. Just output data normally using ASP.NET or Response.Write. If you're doing a POST request, you can check for this in the Page_Load using if (Page.IsPostBack.) Remember that typically you'll only be sending the data for part of a page, not the whole page itself, so you won't need the <html>, <head> or <body> tags.

When I've seen this done in ASP.NET websites before, separate pages have been used for the AJAX calls (e.g. index.aspx = normal site, index-ajax.aspx = ajaxified page component.)

if (Page.IsPostBack)
{
    Response.Write("Hello, world!  From AJAX.");
}

You don't have to use Page.IsPostBack, most AJAX requests are just GET's, so if you put in your Page_Load:

Response.Write("Hello, world!  From AJAX.");

Then do an AJAX call for that page, you'll get "Hello, world! From AJAX." returned from the AJAX call.

Andy Shellam
this works well. Thanks much. I created a separate page which returns exactly what I'm looking for. Thanks much. Although this looks like an inefficient method, judging by what I have read from the other answers. Thanks much!
Tomaszewski
Yeah ideally you'd create your page in ASP markup etc, this was just a quick example. Also like others have said, the ASP.NET AJAX handling is really decent and dead simple (you don't even need a separate page or separate code) - or jQuery is equally as brilliant for the client-side, but as you said this was a learning exercise.
Andy Shellam
A: 

Your best bet would be to create a handler file (*.ashx) that would process the incoming request and return the properly formatted json/xml to the JavaScript. The ScriptManager is used to provide this stuff embedded directly into the actual page, but (unless you intend to rebuild the ScriptManager entirely) you'll find it simpler to do it through a handler and bypass the IIS processing of the standard request.

Joel Etherton
yes you are right. generic handlers are ideal for ajax as it is lightweight than asp.net page.
Adeel
I will definitely try this! Thanks much!
Tomaszewski
A: 

One of the features in ASP.Net is the ability to call server-side code from client-side code without postback, using something called a client callback. There's a couple of minor caveats I've found so far though :-

  • it uses XmlHttp which is IE only at the moment. Firefox and other browsers have an alternative, but the callbacks are using this only.
  • the only type you can return from the server is a string (but we can get around that by serializing if necessary)

The example I've used is where I have two related textboxes that need to be kept in sync. If the ClientID box is changed, the ClientName box should display the name of the client that has that ID, and vice-versa.

To start using this functionality, ensure your code-behind implements the ICallbackEventHandler interface :-

public partial class WebForm1 : System.Web.UI.Page, ICallbackEventHandler

Next, I register my callback methods in the Page_Load method in my aspx.cs :-

// Set up client callbacks. These allow client-side scripts to call
// server-side functions and retrieve the results. Its a string-only
// return I'm afraid, limited by the ICallbackEventHandler method signatures

txtClientID.Attributes.Add("onchange", "GetClientNameById('id|' + this.value, 'id');");

string callBackClientID = Page.ClientScript.GetCallbackEventReference(this, "arg", "ClientNameCallback", "context", "ClientNameCallbackError", true);

string clientIDfunction = "function GetClientNameById(arg,context) { " + callBackClientID + "; }";

Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "GetClientNameById", clientIDfunction, true);

txtClientName.Attributes.Add("onchange", "GetClientIdByName('name|' + this.value, 'name');");

string callBackClientName = Page.ClientScript.GetCallbackEventReference(this, "arg", "ClientIdCallback", "context", "ClientIdCallbackError", true);

string clientNamefunction = "function GetClientIdByName(arg, context) { " + callBackClientName + "; }";

Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "GetClientIdByName", clientNamefunction, true);

This registers the server functions with the page and hooks them up to the client callback methods - these callback methods are basic placeholders that do nothing but give the server somewhere to return its string to. So on the aspx page itself :-

<script type="text/javascript">

function ClientNameCallback(result, context)

{

//sorry about the hardcoded element name...

if(result != "")

document.getElementById('ctl00_ContentPlaceHolder1_txtClientName').setAttribute('value',result);

}

function ClientIdCallback(result,context)

{

//sorry about the hardcoded element name...

if(result != "")

document.getElementById('ctl00_ContentPlaceHolder1_txtClientID').setAttribute('value',result);

}

function ClientNameCallbackError(result, context)

{

//Not sure what error is likely to arise at this point, but...

alert('Error in client name callback function - please say that to eSolutions!');

}

function ClientIdCallbackError(result, context)

{

//Not sure what error is likely to arise at this point, but...

alert('Error in client id callback function - please say that to eSolutions!');

}

</script>

Finally, we implement the required ICallbackEventHandler, which contains the server-side processing we want to perform :-

string ICallbackEventHandler.GetCallbackResult()
{
     return callbackReturnValue;
}

void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{

// eventArgument should be in format field|value,
// e.g., "id|30102" or "name|test client" 
// This is because of the "single string" limitations
// of the callback interface

if(eventArgument.StartsWith("name"))
{
    //....do lookup to get the id based on the name, from an array or database, or whatever
    callbackReturnValue = <string we want to pass back to client-side
}

else if(eventArgument.StartsWith("id"))

etc. etc.

Rob Cowell
An article on this that probably explains it better than me :- http://www.ajaxmatters.com/articles/asp/icallback_intro_p1.aspx
Rob Cowell
i'll have to see if I like this better than using the separate page way described by Andy Shellam
Tomaszewski
+4  A: 

Like this answer, +1 for doing it yourself.

However, I must strongly advise you to use a library like jQuery on the client-side to account for differences across browsers (and in this specific case, there are differences). It (or other libraries) will provide an abstraction you can use across all web browsers to normalize your code.

That being said, in ASP.NET, you could check to see if the call is a post back, and if it is, just write the content to the output stream.

However, I would strongly recommend against that. Rather, the call to ajax should be to another page completely, as it's providing a different purpose, a different kind of response, and therefore, deserves it's own URL.

Also, mind you, that when returning content in the form of XML or JSON (which is typical for Ajax calls, with JSON being pretty dominant now), it's important to change the ContentType property of the response to the appropriate mime type ("text/xml" for XML, "application/json" for JSON).

Note that ASP.NET MVC makes this all much, much easier, and you might want to look into using that instead of the WebForms model, as MVC is built from the ground up to handle many of these scenarios much easier. It allows you to cleanly separate methods which process page rendering, from those that provide functionality in the form of Ajax calls (and this is just one of the many benefits).

casperOne
Yes, after I get the whole idea down of how to do it manually and figure out all the inner workings, I will most likely move over to a library such as jQuery. Thanks much.
Tomaszewski
+1 for pointing out the content-type requirement
Andy Shellam
A: 

If I'm right in my thinking, you can distinguish between a normal HTTP request an an AJAX call by examining the header X-Requested-With.

So, in your toy example if you wanted to respond differently to an AJAX request:

public void Page_Load(object sender, EventArgs e)
{
    if (Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
       Response.Clear(); // dont want <html>.... etc stuff
       Response.Write("Hi from AJAX!");
    }
    else
    {
        // normal page stuff
    }
}

then, in your js, something like this (forgive any syntax errors please)

var req = new XmlHttpRequest();
req.open("GET","default.aspx",false);
req.send("");
document.getElementById('some_div').innerHTML = req.responseXML;
Kirschstein
Hmm.. unfortunately I was not able to get this to work. I'll need to do some more investigating as it seems like an efficient way of doing it. Thanks much!
Tomaszewski
@Tomaszewski which part wasn't working? If you stuck a break point in the CS code was the correct code block firing?
Kirschstein
Hi, thanks for the response. While debugging, my Request.Headers does not seem to have a key called "X-Requested-With", unless I'm missing something.
Tomaszewski
although, i have found this article (http://haacked.com/archive/2009/01/27/aspnetmvc-release-candidate.aspx) and it seems to be talking about that in ASP.NET MVC this works. I have yet to give MVC a try. However, it suggests that WebForms may have a custom "X-Requested-With"... or something like that. Not sure I understand correctly.
Tomaszewski