I'm gonna say that JSONP is definitely the best way to go here, and if you need to allow for the OPTIONS
method in your server side code, you can implement code similar to this. (note: this example is technically for MVC, but can be adapted.)
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", "*");
string rqstMethod = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
if (rqstMethod == "OPTIONS" || rqstMethod == "POST")
{
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Headers", "X-Requested-With, Accept, Access-Control-Allow-Origin, Content-Type");
}
base.OnActionExecuting(filterContext);
}
}
[HttpGet]
[AllowCrossSiteJsonAttribute]
public JsonpResult CommunicateCard(CommunicateCardModel ccm)
{
return ModelState.IsValid
? this.ValidCommunicateCardBuilder(ccm)
: this.InvalidFormSummary(ccm);
}
side note: here's the JsonpResult that I'm using. Works awesome.
namespace System.Web.Mvc
{
#region usings
using System;
using WebService.Attributes;
using WebService.Domain.Models;
using WebService.Exceptions;
using WebService.Models.ViewModels;
using Extensions;
using WebService.Utilities;
#endregion
public class JsonpResult : ActionResult
{
public Object Data { get; set; }
public string JsonCallback { get; set; }
public bool? Success { get; set; }
public override void ExecuteResult(ControllerContext context)
{
// Create a JsonResponse that we can Send out
// Check for the callback parameter
this.JsonCallback = context.HttpContext.Request["callback"];
// if the "callback" doesn't exist, we want to check for "jsoncallback"
if (string.IsNullOrEmpty(this.JsonCallback))
this.JsonCallback = context.HttpContext.Request["jsoncallback"];
// If we've made it this far, we know that the object sent is an
// object that we can serialize and Send back as valid JSON. We now
// need to wrap it in our JsonViewModel for consistancy. NOTE: We're
// using the AsList() extension method to ensure that we're
// returning an Array since our application (Sencha Touch) requires
// all data to be in array format.
if (!Data.GetType().IsGenericType)
Data = Data.AsList();
// Until now we the data is either an ERROR, or null
// if it's null, and the Data is not null, we set Success = true
// If Success is false, it's because we set it to false when we threw an error
// if that's the case, we keep it false for the client.
if (Success == null) Success = true;
var jsonViewModel = new JsonViewModel
{
results = this.Data,
// We know that Success is either True or False so we can
// safely cast the nullable bool to a bool.
success = (bool)this.Success
};
WriteOut(jsonViewModel, context);
}
/// <summary>
/// Write Out the JsonP Response to the Controller.
/// </summary>
/// <param name="jsonResponse">The unserialized object</param>
/// <param name="context">Controller Context</param>
private void WriteOut(JsonViewModel jsonResponse, ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "application/javascript";
response.Write(JsonUtility.Serialize(jsonResponse, this.JsonCallback));
}
}
/// <summary>
/// extension methods for the controller to allow jsonp.
/// </summary>
public static class ContollerExtensions
{
public static JsonpResult Jsonp(this Controller controller, Object data)
{
return new JsonpResult { Data = data };
}
public static JsonpResult Jsonp(this Controller controller, Object data, bool success)
{
// We don't ever want to pass in a "true" success. We only want to
// know if it failed.
if (success)
{
return new JsonpResult { Data = data };
}
return new JsonpResult { Success = false, Data = data };
}
}
}