Showing posts with label Ajax. Show all posts
Showing posts with label Ajax. Show all posts

Feb 3, 2011

jQuery.validation breaks jQuery 1.5 ajax API

Today I found out (the hard way) that the use of the jQuery.validation plugin breaks the jQuery.ajax API. Since the Asp.net MVC 3 uses jQuery.validation for its unobtrusive validation it is included in the default project template. You can imagine my surprise when I updated jQuery to v1.5, and my heavy ajaxified MVC page stopped working correctly. Every ajax request contained an parseerror with an error message stating that the callback jQuery[some random string] was not called. I tried to fiddle with the ajax settings for JSONP (namely jsonp and jsonpCallback), but no matter how I set them, the error was still present.

error_example
Error message

So I started digging around, trying to replicate the problem. When I ran simple html pages $.ajax worked as expected. Then I started to gradually test all the jQuery plugins and found out that the plugin causing the error was jQuery.validation. So here is my reproduction and a simple workaround (the whole project is available here):

The controller:

using System.Linq;
using System.Web.Mvc;

public partial class DefaultController : Controller
{
  private static readonly int[] numbers = Enumerable.Range(1, 10).ToArray();

  public virtual ActionResult Index()
  {
    return View();
  }

  [HttpPost]
  [ValidateAntiForgeryToken]
  public virtual JsonResult SimpleArray(int id)
  {
    return Json(numbers.Concat(new [] { id }));
  }
}

The view:

@{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
  <script src="/Scripts/jquery-1.5.js" type="text/javascript"></script>
  <script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
</head>
  <body>
  <script type="text/javascript">
    $(function () {
      $('a').click(function (e) {
        var req = $.ajax({
            url: '@Url.Action(MVC.Default.SimpleArray())',
            type: 'POST',
            data: $('form').serializeArray(),
            dataType: 'json'
        });
        req.success(function (response, status, xhr) { alert('Success: ' + response); });
        req.error(function (xhr, error, msg) { alert('Error "' + error + '": ' + msg); });
      });
    });
  </script>
  <a href="javascript:void(0);">Do request</a>
  <form action="#">
    <input type="hidden" value="-1" name="id" />
    @Html.AntiForgeryToken()
  </form>
</body>
</html>

The workaround:

$(function () {
$.ajaxSettings.cache = false;
$.ajaxSettings.jsonp = undefined;
$.ajaxSettings.jsonpCallback = undefined;
})


The cause of the problem is this line of JavaScript in jQuery.validate.js, that overrides the settings you pass into the $.ajax call with all the default ones (and jQuery.ajaxSettings defaults to { jsonp: "callback", jsonpCallback: function() {...}}):


// create settings for compatibility with ajaxSetup 
settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings));

Cheers!

Nov 20, 2008

Asp.net Reverse Ajax Chat

1. Motivacija
Ker je bil danes tak turoben dan, sem se odločil napisati demonstracijsko aplikacijo, ki uporablja t.i. reverse ajax, s pomočjo tehnologije Asp.net in WCF. Pred kakim letom sem se udeležil poletne šole, kjer smo podoben primerček spisali v Javi. Danes sem se hotel prepričati ali je kaj podobnega mogoče tudi z asp.net. Gre pa zgolj za predstavitev koncepta, programska koda je zgolj provizorična.

Do kode lahko dostopate preko tega naslova ali kar preko priljubljenega svn klienta:
Uporabniško ime inu geslo se glasi "blog" (brez narekovajev).

2. Kaj je Reverse Ajax?
Predvidevam, da veste kaj je ajax in kako se uporablja. Problem pri klasičnem ajaxu je, da se podatki posredujejo na zahtevo uporabnika. Kot že ime pove pa pri reverse ajaxu gre za obraten koncept, želimo namreč, da se na klientu proži akcija na zahtevo strežnika.

3. Implementacija
Najvažnejša stvar pri implementaciji je zanka preko katere se klient prijavi na obvestila, ki jih potem pošilja strežnik. Strežnik za vsakega klienta hrani vrsto obvestil. Zahtevek za novo obvestilo preveri ali so za klienta na voljo kaka obvestila. Če jih ni zahtevek damo v spanje, dokler za klienta ne prispe novo obvestilo. Pri tem je med strežnikom in klientom ves čas odprt XmlHTTPRequest, preko katerega se (takoj ko je na voljo) pošlje novo obvestilo. Ob sprejetju in obdelavi obvestila klient nemudoma zahteva novo obvestilo.
Spletna storitev - strežnik - izpostavi naslednje metode:

Tukaj je najbolj pomembna metoda GetNextNotification, preko katere klienti zahtevajo nova obvestila. 

Slabost tega pristopa je blokiranje niti spletnega strežnika. Ko je teh čakajočih niti več se delovanje slednjega znatno upočasni.

V izvorni kodi je dodana tudi implementacija ki uporablja t.i. Async Pattern,  kar omogoči lastno asinhrono implementacijo izvajanja zahtevka. Dotična implementacija je kar zanimiva in malce lepše pokomentirana kot "navadna" verzija, tako da priporočam vsem, ki so željni malce bolj poglobljenega znanja, da si kodo tudi ogledajo.


4. Testiranje
Dodana je tudi ena majhna javaskripta za obremenjevanje vašega mlinčka. Zaganjal sem jo v po 3-5ih instancah v vsakem brskalniku (Chrome, IE, Firefox, Opera) in je zadeva delovala solidno, vendar ob 100% obremenjenosti CPU-ja. A upoštevati je potrebno, da vsak klient pošilja 1 sporočilo na sekundo, da se za vsako poslano sporočilo aktivira ~15 spečih niti (ker se je sporočilo pošiljalo vsem odprtim instancam), ter da je vse skupaj potrebno pomnožiti še z ~15! Sporočila so letela s tako hitrostjo, da jih IE ni uspel prikazovati in je nekajkrat popolnoma zamrznil.

5. Zaključek
Reverse ajax je mogoče implementirati tudi s pomočjo  asp.net tehnologije. Žal je java kar se tega tiče veliko pred asp.net saj obstajajo posebaj za te namene razviti strežniki (recimo putty) ter ogrodja (DWR). Obstajajo pa tudi implementacije s pomočjo Flasha in Java Appletov, kjer komunikacija poteka preko Socketov, ti vtičniki pa ob sprejemu obvestil kličejo javascript na klientovem spletnem brskalniku. Naslednjič se bom najverjetnje lotil iste naloge s pomočjo Silverlight-a in WCF-a z uporabo povratnih klicev. Stay tuned ;)