Archive for the ‘AJAX’ Category

Inline AJAX DropDown and Text Editing with Asp.Net MVC and jQuery

Monday, July 12th, 2010

including how to use a database to populate the dropdown

First thing is first, you will need to download jQuery and the Jeditable plugin (I prefer to refer to it as the Jedi-Table!). Be sure to put these references in your View (or Masterpage). Next, you have to set up a view on which to use an inline edit. I find that I often want to use this approach on tables of information. For this View, I will set it to use an IEnumerable of an Item I have called 'ItemOwner' (this is arbitrary and does not really matter). It will be a simple table that lists the Name and the Country of the owner, both of which will be editable inline. Here is the Index in my ExampleController.cs:
myDataContext db = new myDataContext();
public ActionResult Index()
{
    // get the info for the 'Countries' dropdown:
    ViewData["countries"] = db.Countries
        .Select(x => new SelectListItem() 
        { 
            Text = x.Name, 
            Value = x.Id.ToString() 
        }).ToJson();

    // get the 'ItemOwners' I am interested in:
    var owners = db.ItemOwners.Take(3);

    return View(owners);
}

As you can see there, I am also pulling the countries from the database and throwing them into the ViewState - we will get to this later. Since the Country is actually a foreign key relation, the value is set to an integer which is the identity field in the database. It is also using a .ToJson() extension which takes a IEnumerable<SelectListItem> and puts it into a simple JSON string that I use which is here:
public static string 
    ToJson(this IEnumerable<SelectListItem> slis)
{
    string output = "{";
    if (slis != null)
    {
        for (int i = 0; i < slis.Count(); i++)
        {    
            output += " '" + slis.Skip(i)
            .First().Value + "': '" + 
            slis.Skip(i).First().Text + "'" + 
            (i == slis.Count() - 1 ? " " : ",");
        }
    }
    return output += "}";
}

There is probably a better way to do that... but I don't know it?!

I am also pulling 3 ItemOwners from the database, I know this is silly, but it just an example. Here is how I am displaying them in the view:
<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Country</th>
        </tr>
    </thead>
    <tbody>
        <% foreach(var owner in Model) { %>
        <tr>
            <td><%= owner.Name %></td>
            <td><%= owner.Country.Abbreviation %></td>
        </tr>
        <% } %>
    </tbody>
</table>

Now that there is a simple table we want to make it a bit more interactive. Since we aregoing to make all of these fields editable, we need to add in a way to distinguish exactly what they are. To do that, we will need two things: the id of the item they are editing, and the type of inline editing we will be doing (i.e. dropdown or text input). So to do that, let's add in a few css classes and an identifieng ID:
<td id="name<%= owner.Id %>" class="editable text">
    <%= owner.Name %></td>
<td id="ctry<%= owner.Id %>" class="editable dropdown">
    <%= owner.Country.Abbreviation %></td>

And now add a little css to make them appear to be clickable:
td.editable:hover 
{ cursor:pointer; background-color:Orange; }

Now they all look like you can click on them, so we can move on to making the click actually do something.

This is where the jQuery comes in, and it is very simple. I have made these 'helper' methods in Javascript to make all of my inline calls centrally controllable, I keep this in my sites script folder so if I change one inline edit, I change them all; it also makes for more readable Javascript on each page.
function InlineDropdown(collectionToDropDown, ajaxAddress, dropDownDataSet) {
    collectionToDropDown.editable(ajaxAddress,
    {
        data: dropDownDataSet,
        type: 'select',
        indicator: 'saving...',
        tooltip: 'click to edit...',
        submit: 'Save',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextbox(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        indicator: 'saving...',
        tooltip: 'click to edit...',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextarea(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        type        : 'textarea',
        rows        : 4,
        indicator   : 'saving...',
        tooltip     : 'click to edit...',
        style       : 'inherit',
        submit      : 'Save',
        onblur      : 'ignore',
        placeholder : 'click to edit'
    });
}

Obviously you can read all about the options on the Jeditable page, but this is how I set them. Also notice I have a InineTextarea included as well for a textarea which is not covered here but works the exact same.

Now the jQuery calls are almost trivial:
InlineTextbox(
    $('td.editable.text'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>"
);

InlineDropdown(
    $('td.editable.dropdown'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>", 
    <%= ViewData["countries"].ToString() %>
);

What that is doing is sending the POST requests to the specified address. The POST contains a few things:
  • id - the id of the element that sent the request
  • value - the new value passed by the element
We are also passing more information there - remember that we passed both the type of field to edit and the id of the ItemOwner to edit, ie [name837] which emans we want to edit the Name field of ItemOwner 837. So we simply set up an ashx handler (which we specified above) to do the dirty work:
public void ProcessRequest(HttpContext context)
{
    string newValue;
    try
    {
        myDataContext db = new myDataContext();
        string elementId = context.Request.Form["id"];

        // since we made the first 4 of the id the 'field' whic to edit
        // we can just pull the first 4 letters for use in our switch:
        string fieldToEdit = elementId.Substring(0, 4);

        //now take anything after those 4 and it is the Id:
        int idToEdit = Convert.ToInt32(elementId.Remove(0, 4));

        // the value is simply a string:
        newValue = context.Request.Form["value"].Trim();

        // now that we have the id, get the ItemOwner from the db
        ItemOwner owner = db.ItemOwners.FirstOrDefault(x => x.Id == idToEdit);

        // after all is said and done, we will return newValue to the user so the field
        // looks as if the change has taken place (which it has)

        // using the field we pulled above, decide what to do:
        switch (fieldToEdit)
        {
            // name is easy
            case "name": owner.Name = newValue; break;

            // since the country is an integer foreign key, we need to Convert.ToInt32:
            case "ctry":
                owner.CountryId = Convert.ToInt32(newValue);
                // now that we have recorded the value, we want to return the text to
                // the user and not the id value which would make no sense
                newValue = db.Countries.FirstOrDefault(x => x.Id == owner.CountryId).Abbreviation;
                break;
            // if it wasn't caught, something is wrong:
            default: throw new Exception("invalid fieldToEdit passed");
        }

        db.SubmitChanges(); // save it
    }
    // now if an exceptions were reported, the user can see what happened
    // this also inform the user nothing was saved
    // you could easily make this not reported to the user and logged elsewhere
    catch (Exception ex) 
    { newValue = "Error: " + ex.Message + " [nothing written to db]"; }

    //now return what you want in the element:
    context.Response.Write(newValue);       
}

And that is all it takes.

Inline AJAX DropDown and Text Editing with Asp.Net MVC and jQuery

Monday, July 12th, 2010

including how to use a database to populate the dropdown

First thing is first, you will need to download jQuery and the Jeditable plugin (I prefer to refer to it as the Jedi-Table!). Be sure to put these references in your View (or Masterpage). Next, you have to set up a view on which to use an inline edit. I find that I often want to use this approach on tables of information. For this View, I will set it to use an IEnumerable of an Item I have called 'ItemOwner' (this is arbitrary and does not really matter). It will be a simple table that lists the Name and the Country of the owner, both of which will be editable inline. Here is the Index in my ExampleController.cs:
myDataContext db = new myDataContext();
public ActionResult Index()
{
    // get the info for the 'Countries' dropdown:
    ViewData["countries"] = db.Countries
        .Select(x => new SelectListItem() 
        { 
            Text = x.Name, 
            Value = x.Id.ToString() 
        }).ToJson();

    // get the 'ItemOwners' I am interested in:
    var owners = db.ItemOwners.Take(3);

    return View(owners);
}

As you can see there, I am also pulling the countries from the database and throwing them into the ViewState - we will get to this later. Since the Country is actually a foreign key relation, the value is set to an integer which is the identity field in the database. It is also using a .ToJson() extension which takes a IEnumerable<SelectListItem> and puts it into a simple JSON string that I use which is here:
public static string 
    ToJson(this IEnumerable<SelectListItem> slis)
{
    string output = "{";
    if (slis != null)
    {
        for (int i = 0; i < slis.Count(); i++)
        {    
            output += " '" + slis.Skip(i)
            .First().Value + "': '" + 
            slis.Skip(i).First().Text + "'" + 
            (i == slis.Count() - 1 ? " " : ",");
        }
    }
    return output += "}";
}

There is probably a better way to do that... but I don't know it?!

I am also pulling 3 ItemOwners from the database, I know this is silly, but it just an example. Here is how I am displaying them in the view:
<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Country</th>
        </tr>
    </thead>
    <tbody>
        <% foreach(var owner in Model) { %>
        <tr>
            <td><%= owner.Name %></td>
            <td><%= owner.Country.Abbreviation %></td>
        </tr>
        <% } %>
    </tbody>
</table>

Now that there is a simple table we want to make it a bit more interactive. Since we aregoing to make all of these fields editable, we need to add in a way to distinguish exactly what they are. To do that, we will need two things: the id of the item they are editing, and the type of inline editing we will be doing (i.e. dropdown or text input). So to do that, let's add in a few css classes and an identifieng ID:
<td id="name<%= owner.Id %>" class="editable text">
    <%= owner.Name %></td>
<td id="ctry<%= owner.Id %>" class="editable dropdown">
    <%= owner.Country.Abbreviation %></td>

And now add a little css to make them appear to be clickable:
td.editable:hover 
{ cursor:pointer; background-color:Orange; }

Now they all look like you can click on them, so we can move on to making the click actually do something.

This is where the jQuery comes in, and it is very simple. I have made these 'helper' methods in Javascript to make all of my inline calls centrally controllable, I keep this in my sites script folder so if I change one inline edit, I change them all; it also makes for more readable Javascript on each page.
function InlineDropdown(collectionToDropDown, ajaxAddress, dropDownDataSet) {
    collectionToDropDown.editable(ajaxAddress,
    {
        data: dropDownDataSet,
        type: 'select',
        indicator: 'saving...',
        tooltip: 'click to edit...',
        submit: 'Save',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextbox(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        indicator: 'saving...',
        tooltip: 'click to edit...',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextarea(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        type        : 'textarea',
        rows        : 4,
        indicator   : 'saving...',
        tooltip     : 'click to edit...',
        style       : 'inherit',
        submit      : 'Save',
        onblur      : 'ignore',
        placeholder : 'click to edit'
    });
}

Obviously you can read all about the options on the Jeditable page, but this is how I set them. Also notice I have a InineTextarea included as well for a textarea which is not covered here but works the exact same.

Now the jQuery calls are almost trivial:
InlineTextbox(
    $('td.editable.text'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>"
);

InlineDropdown(
    $('td.editable.dropdown'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>", 
    <%= ViewData["countries"].ToString() %>
);

What that is doing is sending the POST requests to the specified address. The POST contains a few things:
  • id - the id of the element that sent the request
  • value - the new value passed by the element
We are also passing more information there - remember that we passed both the type of field to edit and the id of the ItemOwner to edit, ie [name837] which emans we want to edit the Name field of ItemOwner 837. So we simply set up an ashx handler (which we specified above) to do the dirty work:
public void ProcessRequest(HttpContext context)
{
    string newValue;
    try
    {
        myDataContext db = new myDataContext();
        string elementId = context.Request.Form["id"];

        // since we made the first 4 of the id the 'field' whic to edit
        // we can just pull the first 4 letters for use in our switch:
        string fieldToEdit = elementId.Substring(0, 4);

        //now take anything after those 4 and it is the Id:
        int idToEdit = Convert.ToInt32(elementId.Remove(0, 4));

        // the value is simply a string:
        newValue = context.Request.Form["value"].Trim();

        // now that we have the id, get the ItemOwner from the db
        ItemOwner owner = db.ItemOwners.FirstOrDefault(x => x.Id == idToEdit);

        // after all is said and done, we will return newValue to the user so the field
        // looks as if the change has taken place (which it has)

        // using the field we pulled above, decide what to do:
        switch (fieldToEdit)
        {
            // name is easy
            case "name": owner.Name = newValue; break;

            // since the country is an integer foreign key, we need to Convert.ToInt32:
            case "ctry":
                owner.CountryId = Convert.ToInt32(newValue);
                // now that we have recorded the value, we want to return the text to
                // the user and not the id value which would make no sense
                newValue = db.Countries.FirstOrDefault(x => x.Id == owner.CountryId).Abbreviation;
                break;
            // if it wasn't caught, something is wrong:
            default: throw new Exception("invalid fieldToEdit passed");
        }

        db.SubmitChanges(); // save it
    }
    // now if an exceptions were reported, the user can see what happened
    // this also inform the user nothing was saved
    // you could easily make this not reported to the user and logged elsewhere
    catch (Exception ex) 
    { newValue = "Error: " + ex.Message + " [nothing written to db]"; }

    //now return what you want in the element:
    context.Response.Write(newValue);       
}

And that is all it takes.

ASMX ScriptService mistake – Invalid JSON primitive

Monday, May 31st, 2010

One group of searches that consistently brings traffic here is variations on the error: Invalid JSON primitive. Unfortunately, the post that Google sends that traffic to doesn’t address the issue until somewhere within its 150+ comments.

Today, the topic gets its own post.

If you’ve worked with ASMX ScriptServices or Page Methods without ASP.NET AJAX’s client-side proxy (e.g. using jQuery or pure XMLHttpRequest code), you’ve may have seen this cryptic error yourself. Or, perhaps you’ve just arrived here due to seeing it for the first time.

Either way, you may be surprised to learn that the most common reason for this error is that you’ve lied to ASP.NET during your AJAX request.

It all begins with the Content-Type

HTTP’s Content-Type header is a fundamental aspect of communication between browsers and servers, yet often remains hidden from us in day-to-day development. The Content-Type header allows an HTTP connection to describe the format of its contents, using Internet media types (also known as MIME types). A few common ones that you’ve probably seen before are text/html, image/png, and the more topical application/json.

Without the flexible negotiation process content types provide, your users’ browsers and your version of IIS would have to both be “ASMX Compatible” and “JSON Compatible” in order for ScriptServices to function. What a nightmare that would be! The IE6 difficulties we face today would pale in comparison.

Further, Content-Type negotiation is part of what allows a single URL, such as WebService.asmx, to represent data in more than one format (e.g. XML and JSON in ASMX’s case).

The benefits of Content-Type negotiation are well worth a bit of occasional hassle.

Okay, but why does that matter?

When your browser sends a POST request, the W3C’s recommendation is that it should default to using a Content-Type of application/x-www-form-urlencoded. The HTML 4.01 spec describes that serialization scheme:

This is the default content type. Forms submitted with this content type must be encoded as follows:

  1. [Omitted for brevity; not relevant to this post.]
  2. The control names/values are listed in the order they appear in the document. The name is separated from the value by ‘=’ and name/value pairs are separated from each other by ‘&’.

For an example of what that means, consider this simple form:

<form method="post">
  <label>First Name</label>
  <input id="FirstName" value="Dave" name="FirstName" />
 
  <label>Last Name</label>
  <input id="LastName" value="Ward" name="LastName" />
</form>

When the preceding form is submitted with URL encoded serialization, the request’s POST data will look like this:

Firebug screenshot showing the URLEncoded POST data

That standardized serialization format allows a server-side backend like ASP.NET to decipher a form submission’s contents and give you access to each key/value pair. Regardless of what sort of browser submits a form to the server, the Content-Type facilitates a predictable conversion from POST data to server-side collection.

In other words, the Content-Type corresponds to a serialization scheme.

What does that have to do with JSON Primitives?

Understanding Content-Type negotiation and how it relates to serialization is important due to its role in coaxing JSON out of ASMX ScriptServices. Specifically, the fact that you must set a Content-Type of application/json on the request means you’re instructing ASP.NET to interpret your input parameters as JSON serialized data.

However, the W3C’s mandate of URL encoding by default means that most AJAX libraries default to that serialization scheme. Similarly, AJAX tutorials targeting endpoints other than ASMX ScriptServices (including even ASP.NET MVC examples) will describe sending URL encoded data to the server.

In other words, when you’re working with a client-side object like this:

var Person = { FirstName: 'Dave', 
               LastName:  'Ward' }

The default serialization scheme makes it easy to inadvertently transmit that data to the server as a URL encoded string:

FirstName=Dave&LastName=Ward

Again, remember that a Content-Type of application/json is a requirement when working with ASMX ScriptServices. By setting that Content-Type on the request, you’ve committed to sending JSON serialized parameters, and a URL encoded string is far from valid JSON.

In fact, it’s invalid JSON (primitive?), hence the cryptic error message.

Instead of the URL encoded string above, you must be sure to send a JSON string:

{'FirstName':'Dave','LastName':'Ward'}

Whether you’re using XMLHttpRequest directly or a JavaScript library that abstracts the details, getting your request’s serialization wrong is the root of the invalid JSON primitive error. However, a more specific issue tends to be the leading cause of this happening.

When good JavaScript libraries go bad

The most common source of this error stems from a subtlety of using jQuery’s $.ajax() method to call ASMX ScriptServices. Cobbling together snippets of code from the documentation, platform agnostic tutorials, and even posts here on my site, it’s easy to end up with something like this:

// WRONG!
$.ajax({
  type: 'POST',
  contentType: 'application/json',
  dataType: 'json',
  url: 'WebService.asmx/Hello',
  data: { FirstName: "Dave", LastName: "Ward" }
});

Notice the JavaScript object literal being supplied to $.ajax()’s data parameter. That appears vaguely correct, but will result in the invalid JSON primitive error.

Why? jQuery serializes $.ajax()’s data parameter using the URL encoded scheme, regardless of what Content-Type is specified. Even though the contentType parameter clearly specifies JSON serialization, this URL encoded string is what jQuery will send to the server:

FirstName=Dave&LastName=Ward

That obviously isn’t valid JSON!

The solution is as simple as two single-quotes:

// RIGHT
$.ajax({
  type: 'POST',
  contentType: 'application/json',
  dataType: 'json',
  url: 'WebService.asmx/Hello',
  data: '{ FirstName: "Dave", LastName: "Ward" }'
});

Did you spot the difference?

Instead of a JavaScript object literal, the data parameter is a JSON string now. The difference is subtle, but helpful to understand. Since it’s a string, jQuery won’t attempt to perform any further transformation, and the JSON string will be unimpeded as it is passed to the ASMX ScriptService.

It doesn’t have to be this way

The problem is trivial once you’re aware of the underlying issue, but there’s not a great reason I can see why things need to be this way in the first place. Either half of this equation could easily provide a remedy.

jQuery – I believe the most correct solution would be $.ajax() attempting to honor the serialization scheme indicated by its contentType parameter. In the case of application/json fixing this could be easy as testing for JSON.stringify and using it if available, to avoid adding any complexity/size to jQuery core.

That would leave it our responsibility to reference a copy of json2.js in older browsers, but that convention wouldn’t be much of a burden. We generally do that anyway when the client-side objects get complex.

Microsoft – It’s absolutely correct that the framework throws an error when you lie to it about what you’re sending. However, a bit of leniency could potentially save thousands of hours spent troubleshooting this problem (if my search traffic is any indication of its prevalence).

Is there any reason that the ScriptHandlerFactory can’t intelligently differentiate between between JSON and URL encoded inputs? If the first non-whitespace character of the request isn’t an opening curly brace, why not attempt to deserialize it as URL encoded before throwing an invalid JSON primitive error?

###

Originally posted at Encosia. If you're reading this elsewhere, come on over and see the original.



ASMX ScriptService mistake – Invalid JSON primitive

ASMX ScriptService mistakes: Installation and configuration

Monday, March 8th, 2010

Continuing my series of posts about ASMX services and JSON, in this post I’m going to cover two common mistakes that plague the process of getting a project’s first ASMX ScriptService working: Installing System.Web.Extensions into the GAC and configuring your web.config.

System.Web.Extensions (aka ASP.NET AJAX)

The ability for ASMX services to return raw JSON is made possible by two key features originally added by the ASP.NET AJAX Extensions v1.0:

  • JavaScriptSerializerThe JavaScriptSerializer class is the actual workhorse that translates back and forth between JSON strings and .NET CLR objects. Though less powerful than WCF’s DataContractJsonSerializer and third-party libraries like Json.NET, JavaScriptSerializer is likely all you’ll ever need for simple AJAX callbacks.
  • ScriptHandlerFactory – There are several more classes behind the scenes*, but the ScriptHandlerFactory is the tip of the iceberg that you’ll need to remember during configuration. Redirecting ASMX requests through this HttpHandler is what coordinates the pairing of ScriptService with JavaScriptSerializer to provide automatic JSON handling.

Though both of these classes appear in the System.Web.Script namespace, they actually reside in ASP.NET AJAX’s System.Web.Extensions assembly. That has different implications depending on which version of ASP.NET your site targets:

  • 1.x – No support for ScriptServices. A custom HttpHandler coupled with a third party library like Json.NET is your best bet (if anyone has a good tutorial on doing this under 1.x, let me know so that I can link to it).
  • 2.0 – ScriptServices are available in ASP.NET 2.0 with the installation of the ASP.NET AJAX Extensions v1.0.
    • That means that the ASP.NET AJAX installer needs to be run on the server that hosts your site, not just on your local development machine.
    • For some of a ScriptService’s features to work in medium trust (i.e. shared hosting), the System.Web.Extensions assembly needs to be in your server’s global assembly cache (GAC). Don’t waste your time trying to make it work in your site’s /bin directory; insist that the extensions be properly installed on the server.
  • 3.5+ – As of .NET 3.5, System.Web.Extensions ships with the framework. No additional assemblies need be installed.

* If you’re interested in the internals, I highly recommend downloading the ASP.NET AJAX Extensions v1.0 source and taking a look at ScriptHandlerFactory, RestHandlerFactory, and RestHandler. Though the classes have changed slightly since v1.0, they are still very similar.

Rerouting the ASMX handler via web.config

With the System.Web.Extensions assembly installed in the GAC, the remaining configuration step is an element in your site’s web.config. To take advantage of the ScriptService functionality, ASP.NET must be instructed to reroute ASMX requests through the ScriptHandlerFactory instead of ASP.NET’s standard ASMX handler.

This step is often unnecessary. The project templates in ASP.NET 3.5+ include all the necessary configuration elements, and ASP.NET 2.0 sites created with the “AJAX Enabled” templates are also pre-configured correctly.

However, if you find yourself unable to coax JSON out of an ASMX ScriptService, verifying your web.config is one of the best first steps in troubleshooting the issue. Whether due to a web.config generated by an older project template, accidental modification, or other issues, missing the httpHandlers web.config setting is a very common pitfall.

What should appear varies slightly depending on which version of ASP.NET your project targets. Regardless of your framework version, the config elements should be added to the <httpHandlers> section and are the only elements necessary. The variety of other config items required for the UpdatePanel and ScriptManager aren’t crucial to the ScriptService functionality.

ASP.NET 2.0 (with the ASP.NET AJAX Extensions installed)

<configuration>
  <system.web>
    <httphandlers>
      <remove path="*.asmx" verb="*" />
      <add path="*.asmx" verb="*" Culture=neutral, validate="false"
           type="System.Web.Script.Services.ScriptHandlerFactory, 
                 System.Web.Extensions, Version=1.0.61025.0,
                 PublicKeyToken=31bf3856ad364e35" />
    </httphandlers>
  </system.web>
</configuration>

ASP.NET 3.5

<configuration>
  <system.web>
    <httphandlers>
      <remove path="*.asmx" verb="*" />
      <add path="*.asmx" verb="*" Culture=neutral, validate="false"
           type="System.Web.Script.Services.ScriptHandlerFactory, 
                 System.Web.Extensions, Version=3.5.0.0,
                 PublicKeyToken=31bf3856ad364e35" />
    </httphandlers>
  </system.web>
</configuration>

ASP.NET 4

Thankfully, ASP.NET 4 has taken steps to reverse the trend of ever-enlarging baseline web.config files. By moving common configuration items such as the ScriptService’s HttpHandler to the default machine.config, each individual site need not include those configuration elements in their specific web.config files.

Unless you go out of your way to manually remove their HttpHandler, ASMX ScriptServices will work automatically in any ASP.NET 4 site.

###

Originally posted at Encosia. If you're reading this elsewhere, come on over and see the original.



ASMX ScriptService mistakes: Installation and configuration

ASMX and JSON – Common mistakes and misconceptions

Wednesday, March 3rd, 2010

While we were recording episode 5 of Mastering jQuery, I found myself running down a lengthy list of misconceptions and potential pitfalls when it comes to using ASMX services for AJAX callbacks. After years of fielding questions revolving around that topic, I suppose I’ve developed a decent handle on the issues most often encountered.

To preemptively surface some of that commonly requested information, I’m going to publish a series of relatively short posts, each describing one mistake or misconception that I’ve seen come up frequently.

To get started, I want to cover one of the most fundamental of these misconceptions: That ASMX services can’t return JSON.

Misconception: ASMX services are limited to XML

One of the most stubbornly persistent misconceptions about ASMX services is the rumor that they are limited to returning XML. With that notion mind, many developers understandably avoid them for client-side AJAX callbacks. When every byte counts, raw JSON is always preferable to the bloat of XML.

However, the introduction of ASP.NET AJAX removed that XML limitation.

In any ASP.NET 2.0+ AJAX enabled site, one of ASP.NET AJAX’s additions is something called the ScriptService. When a ScriptService is called in the correct manner, it automatically returns its result serialized as JSON instead of XML.

In fact, these ASMX ScriptServices even accept their parameters as JSON.

The ASP.NET AJAX “ScriptService”

If you’ve created an ASMX service in the past few years, you’ve probably seen this blurb at the beginning of the default template:

// To allow this Web Service to be called from script, 
//   using ASP.NET AJAX, uncomment the following line. 
// [System.Web.Script.Services.ScriptService] 
public class WebService : System.Web.Services.WebService {

Since it never explicitly mentions JSON and implies a tight coupling with ASP.NET AJAX, it’s easy to understand why the ScriptService’s true power sometimes goes unnoticed. Thankfully, that attribute does much more than simply expose ASP.NET AJAX specific functionality.

In fact, the ScriptService attribute enables all of an ASMX service’s methods to respond with raw JSON if they are requested correctly. For example, these ScriptServices can easily send and receive JSON in conjunction with a third party library, without a ScriptManager or MicrosoftAjax.js anywhere to be seen.

Two simple requirements

As I alluded to earlier, the one stipulation is that these ScriptServices only return JSON serialized results if they are requested properly. Otherwise, even a service marked with the attribute will return XML instead of JSON. I can only assume that’s part of the reason for the misconception that ASMX services cannot respond with JSON.

Scott Guthrie has a great post on the specific requirements for coercing JSON out of ScriptServices. To summarize that, requests to the service methods must meet two requirements:

  • Content-Type – The HTTP request must declare a content-type of application/json. This informs the ScriptService that it will receive its parameters as JSON and that it should respond in kind.
  • HTTP Method – By default, the HTTP request must be a POST request. It is possible to circumvent this requirement, but it is advisable to stick with HTTP POST requests when dealing with JSON.

That’s it.

As long as those two requirements are satisfied, anything from low-level XMLHttpRequest code, to third-party libraries like jQuery, to ASP.NET AJAX itself can easily retrieve JSON serialized data from ASMX services.

###

Originally posted at Encosia. If you're reading this elsewhere, come on over and see the original.



ASMX and JSON – Common mistakes and misconceptions