Cross-domain JSONP with jQuery call step-by-step guide

I’ve been banging my head all day to accomplish this, it’s like a puzzle.. but since i get it to work i thought i could write this post for you and myself.

What we want to accomplish?

Simple way to communicate cross-domain with ASMX .NET 3.5 Web Service

How can we do it?

1. Implement a web service method like the following

   [ScriptService]
   public class JSONP_EndPoint : System.Web.Services.WebService
   {
       [WebMethod]
       [ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json)]
       public string Sum(string x,string y)
       {
           return x + y;
       }
   }

2. Add New class library with a name ContentTypeHttpModule

The reason for this is no matter how you specify the content-type of your ajax call ASP.NET send the request with Content-Type text/xml; charset=utf-8 this is security feature explained here by ScottGu 

3. Add the following code to your Class (Code by Jason i just did a simple modification)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;

namespace ContentTypeHttpModule
{
    public class ContentTypeHttpModule : IHttpModule
    {
        private const string JSON_CONTENT_TYPE = "application/json; charset=utf-8";

        #region IHttpModule Members
        public void Dispose()
        {
        }

        public void Init(HttpApplication app)
        {
            app.BeginRequest += OnBeginRequest;
            app.ReleaseRequestState += OnReleaseRequestState;
        }
        #endregion

        public void OnBeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpRequest resquest = app.Request;
            if (!resquest.Url.AbsolutePath.Contains("JSONP-EndPoint.asmx")) return;

            if (string.IsNullOrEmpty(app.Context.Request.ContentType))
            {
                app.Context.Request.ContentType = JSON_CONTENT_TYPE;
            }
        }

        public void OnReleaseRequestState(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpResponse response = app.Response;
            if (app.Context.Request.ContentType != JSON_CONTENT_TYPE) return;

            response.Filter = new JsonResponseFilter(response.Filter);
        }
    }

    public class JsonResponseFilter : Stream
    {
        private readonly Stream _responseStream;
        private long _position;

        public JsonResponseFilter(Stream responseStream)
        {
            _responseStream = responseStream;
        }

        public override bool CanRead { get { return true; } }

        public override bool CanSeek { get { return true; } }

        public override bool CanWrite { get { return true; } }

        public override long Length { get { return 0; } }

        public override long Position { get { return _position; } set { _position = value; } }

        public override void Write(byte[] buffer, int offset, int count)
        {
            string strBuffer = Encoding.UTF8.GetString(buffer, offset, count);
            strBuffer = AppendJsonpCallback(strBuffer, HttpContext.Current.Request);
            byte[] data = Encoding.UTF8.GetBytes(strBuffer);
            _responseStream.Write(data, 0, data.Length);
        }

        private string AppendJsonpCallback(string strBuffer, HttpRequest request)
        {
            return request.Params["callback"] +"(" + strBuffer + ");";
        }

        public override void Close()
        {
            _responseStream.Close();
        }

        public override void Flush()
        {
            _responseStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _responseStream.Seek(offset, origin);
        }

        public override void SetLength(long length)
        {
            _responseStream.SetLength(length);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _responseStream.Read(buffer, offset, count);
        }
    }
}

4. Register the HttpModule in the service project

4.1 Add referance to the HttpModule assembly to the service project

4.2 Add this code to web.config to register the module

<add name="ContentTypeHttpModule"
                    type="ContentTypeHttpModule.ContentTypeHttpModule, ContentTypeHttpModule" />

This goes under system.web / httpmodules section

5. Add a web project for testing the application

5.1 add the following libs

jquery-1.3.1.js

json2.js

5.2 add new script file caller.js

function test() {
    $.ajax({ url: "http://localhost:1690/JSONP-EndPoint.asmx/Sum",
    data: { x: JSON.stringify("Now i am getting jsop string"), y: JSON.stringify("2nd param") },
        dataType: "jsonp",
        success: function(json) {
            alert(json.d);
        },
        error: function() {
            alert("Hit error fn!");
        }
    });
}
5.3 Add referances to jquery-1.3.1.js and json2.js
5.4 Add Default.aspx page with input button that has onclick=”return test();”

6. Remarks

6.1 I use the JSON.stringify function to serialize the string data parameters.

6.2 .d is a security features on ASP.NET 3.5

Download the code

Published Fri, Aug 14 2009 1:36 AM by Adel Khalil

Comments

# How to make cross-domain jsonp call with jquery step by step

Friday, August 14, 2009 12:52 AM by DotNetKicks.com

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Simple way to communicate cross-domain with ASMX .NET 3.5 Web Service

Friday, August 14, 2009 5:17 AM by DotNetShoutout

Thank you for submitting this cool story - Trackback from DotNetShoutout

# Cross-domain JSONP with jQuery call step-by-step guide

Friday, August 14, 2009 8:56 AM by WebDevVote.com

You are voted (great) - Trackback from WebDevVote.com

# Klonopin addiction.

Sunday, October 11, 2009 9:55 AM by Klonopin.

Klonopin withdrawl. Klonopin and suicide. Klonopin for fear of flying. Klonopin.

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, October 20, 2009 6:16 PM by Aim

The article is great! There is one major issue with using buffer - if the response is buffered then each part (buffer) is wrapped with the callback method and that results in an incorrect javascript and response. However it's possible to turn off buffering by adding response.BufferOutput = false; in OnReleaseRequestState. Of course this may have some impact on performance.

# re: Cross-domain JSONP with jQuery call step-by-step guide

Thursday, November 05, 2009 10:25 PM by Zack Wagner

5 stars to you!

You had the simplest solution to this problem! Thank you so much!

Best of all, the solution still works with ASP.NET 2.0 w/ AJAX Extensions (sans the .d in the Javascript call)

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, November 10, 2009 6:35 PM by KiT

I've tried this and it works great in IE.

However, it doesn't work in any other browser for me.

Did I do something wrong?

Do I need json2.js anyway?

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, November 10, 2009 7:48 PM by Adel Khalil

No, it should work on FF, Chrome and IE with no problem? what are you getting from FF, try to debug using FireBug ?

# re: Cross-domain JSONP with jQuery call step-by-step guide

Wednesday, November 11, 2009 1:52 PM by KiT

@Adel:

I tried to connect to a Web service which return a line of text and I want to show that text in a lebel.

Here is the .asmx code:

   [ScriptService]

   public class Events : WebService

   {

       [WebMethod]

       [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]

       public string HelloWorld()

       {

           return "Hello World";

       }

   }

Here is my jQuery code:

   var options = {

       url: "ws.someweb.com/.../HelloWorld",

       data: {},

       dataType: "jsonp",

       success: function(msg) { $("#message").html(msg.d); },

       error: function(msg) { alert(msg); }

   }

   $.ajax(options);

This works just fine in IE, but not in the other browsers I mentioned earlier.

Any idea please?

# re: Cross-domain JSONP with jQuery call step-by-step guide

Sunday, November 15, 2009 8:56 PM by KiT

This is weird. I tried running your project and it works just fine in all browsers. But it somehow doesn't work for me in firefox, opera, and chrome. T-T

# re: Cross-domain JSONP with jQuery call step-by-step guide

Monday, November 16, 2009 6:58 PM by KiT

OK, I finally figured it out.

Using <button> to take the event somehow doesn't work.

After switching to <input>, it did the magic!

Don't know why.

I'll work on this issue to figure out the cause.

Thanks a lot.

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, November 17, 2009 6:38 PM by Adel Khalil

Glad you got it to work.

# Cross-domain JSONP with jQuery call step-by-step guide

Monday, January 04, 2010 5:32 AM by PimpThisBlog.com

Thank you for submitting this cool story - Trackback from PimpThisBlog.com

# re: Cross-domain JSONP with jQuery call with dot net nuke

Tuesday, January 19, 2010 8:35 AM by Zeeshan CH

Thanks for sharing this, I am aspnet developer and i am implementing above scenario in dot net nuke i have done all the settings but still i am unable to call the service any idea?  

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, January 19, 2010 9:11 AM by Zeeshan CH

Great i got it. Its working fine now. You have done a fabulous job Mr, adel.

# re: Cross-domain JSONP with jQuery call step-by-step guide

Tuesday, January 19, 2010 10:20 AM by Adel Khalil

Great that you got it. thanks for the comment.

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Please add 8 and 8 and type the answer here: