使Asp.net WebApi支持JSONP和Cors跨域访问

1.服务端处理

同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝。同源策略以及跨域资源共享在大部分情况下针对的是Ajax请求。同源策略主要限制了通过XMLHttpRequest实现的Ajax请求,如果请求的是一个“异源”地址,浏览器将不允许读取返回的内容。JSONP是一种常用的解决跨域资源共享的解决方案,现在我们利用ASP.NET Web API自身的扩展性提供一种“通用”的JSONP实现方案。

我们通过继承JsonMediaTypeFormatter定义了如下一个JsonpMediaTypeFormatter类型。它的只读属性Callback代表JavaScript回调函数名称,改属性在构造函数中指定。在重写的方法WriteToStreamAsync中,对于非JSONP调用(回调函数不存在),我们直接调用基类的同名方法对响应对象实施针对JSON的序列化,否则调用WriteToStream方法将对象序列化后的JSON字符串填充到JavaScript回调函数中。

我们重写了GetPerRequestFormatterInstance方法,在默认情况下,当ASP.NET Web API采用内容协商机制选择出与当前请求相匹配的MediaTypeFormatter后,会调用此方法来创建真正用于序列化响应结果的MediaTypeFormatter对象。在重写的这个GetPerRequestFormatterInstance方法中,我们尝试从请求的URL中得到携带的JavaScript回调函数名称,即一个名为“callback”的查询字符串。如果回调函数名不存在,则直接返回自身,否则返回据此创建的JsonpMediaTypeFormatter对象。

1.重写JsonMediaTypeFormatter处理JSONP请求

复制代码
public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
    public string Callback { get; private set; }

    public JsonpMediaTypeFormatter(string callback = null)
    {
        this.Callback = callback;
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        if (string.IsNullOrEmpty(this.Callback))
        {
            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
        }
        try
        {
            this.WriteToStream(type, value, writeStream, content);
            return Task.FromResult<AsyncVoid>(new AsyncVoid());
        }
        catch (Exception exception)
        {
            TaskCompletionSource<AsyncVoid> source = new TaskCompletionSource<AsyncVoid>();
            source.SetException(exception);
            return source.Task;
        }
    }

    private void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
    {
        JsonSerializer serializer = JsonSerializer.Create(this.SerializerSettings);
        using (StreamWriter streamWriter = new StreamWriter(writeStream, this.SupportedEncodings.First()))
        using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter) { CloseOutput = false })
        {
            jsonTextWriter.WriteRaw(this.Callback + "(");
            serializer.Serialize(jsonTextWriter, value);
            jsonTextWriter.WriteRaw(")");
        }
    }

    public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
    {
        if (request.Method != HttpMethod.Get)
        {
            return this;
        }
        string callback;
        if (request.GetQueryNameValuePairs().ToDictionary(pair => pair.Key,
                pair => pair.Value).TryGetValue("callback", out callback))
        {
            return new JsonpMediaTypeFormatter(callback);
        }
        return this;
    }

    [StructLayout(LayoutKind.Sequential, Size = 1)]
    private struct AsyncVoid
    { }
}
View Code
复制代码

2.在网站启动注册

复制代码
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
        //注册JSOPN提交处理
        GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter());
    }
}
复制代码

2.客户端实例

复制代码
//使用Cors方式提交
$.get(apiUrl.getTwo("TestTwo"), {
    name: '张三'
}, function (data) {
    alert(data);
});

//使用Jsonp方式提交
$.ajax({
    data: {
        name: 'zhangsan'
    },
    url: apiUrl.getTwo('TestTwo'),
    dataType: 'jsonp',
    success: function (data) {
        alert(data);
    },
    error: function (XMLRequest, textStatus) {
        console.info(XMLRequest);
        console.info(textStatus);
        alert('失败');
    }
});
复制代码

分别的相应结果:

 

posted @   天马3798  阅读(1377)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
历史上的今天:
2013-12-31 Excel操作 Microsoft.Office.Interop.Excel.dll的使用
2013-12-31 c#使用Microsoft Excel 12.0 object Libary导出的Excel文件office2003不能打开!!~~(分享)
2013-12-31 解决第三方DLL没有强签名
2013-12-31 WorkBook的SaveAs方法 2
2013-12-31 关于asp.net C# 导出Excel文件 打开Excel文件格式与扩展名指定格式不一致的解决办法
2013-12-31 ExcelApplication 另存Excel的SaveAs函数
2013-12-31 让VC编译出来的程序不依赖于msvcr80.dll/msvcr90.dll/msvcr100.dll等文件
点击右上角即可分享
微信分享提示