Fork me on GitHub

WebApi跨域

一、跨域问题的由来

 

 

同源策略:出于安全考虑,浏览器会限制脚本中发起的跨站请求,浏览器要求JavaScript或Cookie只能访问同域下的内容。

 

正是由于这个原因,我们不同项目之间的调用就会被浏览器阻止。比如我们最常见的场景:WebApi作为数据服务层,它是一个单独的项目,我们的MVC项目作为Web的显示层,这个时候我们的MVC里面就需要调用WebApi里面的接口取数据展现在页面上。因为我们的WebApi和MVC是两个不同的项目,所以运行起来之后就存在上面说的跨域的问题。

 

二、跨域问题解决原理

 

CORS全称Cross-Origin Resource Sharing,中文全称跨域资源共享。它解决跨域问题的原理是通过向http的请求报文和响应报文里面加入相应的标识告诉浏览器它能访问哪些域名的请求。比如我们向响应报文里面增加这个Access-Control-Allow-Origin:http://localhost:8081,就表示支持http://localhost:8081里面的所有请求访问系统资源。其他更多的应用我们就不一一列举,可以去网上找找。

 

三、现象

 

下面我就结合一个简单的实例来说明下如何使用CORS解决WebApi的跨域问题。

api接口地址:http://127.0.0.1:8004/api/Values/ResturnResult

[HttpGet]
public string ResturnResult()
{
      return "Success";
}

从web站点发送请求:

$(function () {
        $.ajax({
            type: "get",
            url: "http://127.0.0.1:8004/api/Values/ResturnResult",
            data: {},
            success: function (data, status) {
                if (status == "success") {
                    $("#result").html(data);
                }
            },
            error: function (e) {
                $("#result").html("Error");
            },
            complete: function () {

            }

        });
    });

结果:

四、跨域问题解决方案

1、使用CORS跨域(始终会报错,暂时还没有找到解决方案)

在WebApi项目上面使用Nuget搜索“microsoft.aspnet.webapi.cors”,安装

代码如下:

//WebApiConfig中代码
public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //跨域配置
            config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

//脚本
$.ajax({
            type: "get",
            url: "http://localhost:8004/api/Charging/GetAllChargingData",
            data: { _type: 1 },
            success: function (data, status) {
                if (status == "success") {
                    $("#div_test").html(data);
                }
            },
            error: function (e) {
                $("#div_test").html("Error");
            },
            complete: function () {

            }

        });

//webapi
public class ChargingController : ApiController
    {
        /// <summary>
        /// 得到所有数据
        /// </summary>
        /// <returns>返回数据</returns>
        [HttpGet]
        public string GetAllChargingData()
        {
            return "Success";
        }
    }

运行webapi始终报错:(如果您知道解决方案,请指点,多谢!)

2、通过配置文件配置;在IE10之下不可以;

 

 <system.webServer>

<httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Headers" value="Content-Type" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /> </customHeaders> </httpProtocol>
<handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>

3、扩展api:

/// <summary>
    /// Cors特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
    public class CorsAttribute : Attribute
    {
        public Uri[] AllowOrigins { get; private set; }
        public string ErrorMessage { get; private set; }
        public CorsAttribute(params string[] allowOrigins)
        {
            this.AllowOrigins = (allowOrigins ?? new string[0]).Select(origin => new Uri(origin)).ToArray();
        }
        public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)
        {
            headers = null;
            string origin = null;
            try
            {
                origin = request.Headers.GetValues("Origin").FirstOrDefault();
            }
            catch (Exception)
            {
                this.ErrorMessage = "Cross-origin request denied";
                return false;
            }
            Uri originUri = new Uri(origin);
            if (this.AllowOrigins.Contains(originUri))
            {
                headers = this.GenerateResponseHeaders(request);
                return true;
            }

            this.ErrorMessage = "Cross-origin request denied";
            return false;
        }

        private IDictionary<string, string> GenerateResponseHeaders(HttpRequestMessage request)
        {

            //设置响应头"Access-Control-Allow-Methods"

            string origin = request.Headers.GetValues("Origin").First();

            Dictionary<string, string> headers = new Dictionary<string, string>();

            headers.Add("Access-Control-Allow-Origin", origin);

            if (request.IsPreflightRequest())
            {
                //设置响应头"Access-Control-Request-Headers"
                //和"Access-Control-Allow-Headers"
                headers.Add("Access-Control-Allow-Methods", "*");

                string requestHeaders = request.Headers.GetValues("Access-Control-Request-Headers").FirstOrDefault();

                if (!string.IsNullOrEmpty(requestHeaders))
                {
                    headers.Add("Access-Control-Allow-Headers", requestHeaders);
                }
            }
            return headers;
        }
    }

    /// <summary>
    /// HttpRequestMessage扩展方法
    /// </summary>
    public static class HttpRequestMessageExtensions
    {
        public static bool IsPreflightRequest(this HttpRequestMessage request)
        {
            return request.Method == HttpMethod.Options
                && request.Headers.GetValues("Origin").Any()
                && request.Headers.GetValues("Access-Control-Request-Method").Any();
        }
    }

 

 /// <summary>
    /// 跨域资源访问的HTTP处理程序
    /// </summary>
    public class CorsMessageHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //得到描述目标Action的HttpActionDescriptor
            HttpMethod originalMethod = request.Method;
            bool isPreflightRequest = request.IsPreflightRequest();
            if (isPreflightRequest)
            {
                string method = request.Headers.GetValues("Access-Control-Request-Method").First();
                request.Method = new HttpMethod(method);
            }

            HttpConfiguration configuration = request.GetConfiguration();
            HttpControllerDescriptor controllerDescriptor = configuration.Services.GetHttpControllerSelector().SelectController(request);
            HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request)
            {
                ControllerDescriptor = controllerDescriptor
            };
            HttpActionDescriptor actionDescriptor = configuration.Services.GetActionSelector().SelectAction(controllerContext);
            //根据HttpActionDescriptor得到应用的CorsAttribute特性
            CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault() ??
                controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();
            if (null == corsAttribute)
            {
                return base.SendAsync(request, cancellationToken);
            }
            //利用CorsAttribute实施授权并生成响应报头
            IDictionary<string, string> headers;
            request.Method = originalMethod;
            bool authorized = corsAttribute.TryEvaluate(request, out headers);
            HttpResponseMessage response;
            if (isPreflightRequest)
            {
                if (authorized)
                {
                    response = new HttpResponseMessage(HttpStatusCode.OK);
                }
                else
                {
                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);
                }
            }
            else
            {
                response = base.SendAsync(request, cancellationToken).Result;
            }

            //添加响应报头
            if (headers != null && headers.Any())
                foreach (var item in headers)
                    response.Headers.Add(item.Key, item.Value);

            return Task.FromResult<HttpResponseMessage>(response);
        }
    }
 GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsMessageHandler());
IHttpActionResult的使用,需要安装 Microsoft.AspNet.WebApi;
[Cors("http://localhost:10154")]
    public class ContactsController : ApiController
    {
        public IHttpActionResult GetAllContacts()
        {
            Contact[] contacts = new Contact[]
             {
                 new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
                 new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},
                 new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},
             };
            return Json<IEnumerable<Contact>>(contacts);
        }
    }
    public class Contact
    {
        public string Name { get; set; }
        public string PhoneNo { get; set; }
        public string EmailAddress { get; set; }
    }

关于CORS详解,参见:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-02.html

webapi系列文章:http://www.cnblogs.com/r01cn/tag/ASP.NET%20Web%20API/default.html?page=1

posted @ 2016-04-30 11:00  迁梦余光  阅读(709)  评论(0编辑  收藏  举报