日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

WebApi系列~在WebApi中實(shí)現(xiàn)Cors訪問(wèn)

 昵稱(chēng)10504424 2014-02-24

說(shuō)在前
 
Cors是個(gè)比較熱的技術(shù),這在蔣金楠的博客里也有體現(xiàn),Cors簡(jiǎn)單來(lái)說(shuō)就是“跨域資源訪問(wèn)”的意思,這種訪問(wèn)我們指的是Ajax實(shí)現(xiàn)的異步訪問(wèn),形象點(diǎn)說(shuō)就是,一個(gè)A網(wǎng)站公開(kāi)一些接口方法,對(duì)于B網(wǎng)站和C網(wǎng)站可以通過(guò)發(fā)Xmlhttprequest請(qǐng)求來(lái)調(diào)用A網(wǎng)站的方法,對(duì)于xmlhttprequest封裝比較好的插件如jquery的$.ajax,它可以讓開(kāi)發(fā)者很容易的編寫(xiě)AJAX異步請(qǐng)求,無(wú)論是Get,Post,Put,Delete請(qǐng)求都可以發(fā)送。
 
Cors并不是什么新的技術(shù),它只是對(duì)HTTP請(qǐng)求頭進(jìn)行了一個(gè)加工,還有我們的Cors架構(gòu)里,對(duì)jsonp也有封裝,讓開(kāi)發(fā)者在使用jsonp訪問(wèn)里,編寫(xiě)的代碼量更少,更直觀,呵呵。(Jsonp和Json沒(méi)什么關(guān)系,它是從一個(gè)URI返回一個(gè)Script響應(yīng)塊,所以,JSONP本身是和域名沒(méi)關(guān)系的,而傳統(tǒng)上的JSON是走xmlhttprequest的,它在默認(rèn)情況下,是不能跨域訪問(wèn)的)
 
做在后
 
一  下面先說(shuō)一下,對(duì)jsonp的封裝:
 
1 注冊(cè)jsonp類(lèi)型,在global.asax里Application_Start方法中
 
 GlobalConfiguration.Configuration.Formatters.Insert(0, new EntityFrameworks.Web.Core.JsonpMediaTypeFormatter());
2 編寫(xiě)JsonpMediaTypeFormatter這個(gè)類(lèi)型中實(shí)現(xiàn)了對(duì)jsonp請(qǐng)求的響應(yīng),并在響應(yīng)流中添加指定信息,如callback方法名。
 


    /// <summary>
    /// 對(duì)jsonp響應(yīng)流的封裝
    /// </summary>
    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
        {
        }

    }


二  對(duì)指定域名實(shí)現(xiàn)友好的跨域資源訪問(wèn):
 
1 在global.asax中注冊(cè)這個(gè)HttpHandler,使它對(duì)HTTP的處理進(jìn)行二次加工,它可以有同步和異步兩個(gè)版本,本例中實(shí)現(xiàn)異步方式實(shí)現(xiàn)
 
 //對(duì)指定URI的網(wǎng)站進(jìn)行跨域資源的共享
 GlobalConfiguration.Configuration.MessageHandlers.Add(new EntityFrameworks.Web.Core.Handlers.CorsMessageHandler());
下面是MessageHandlers原代碼,實(shí)現(xiàn)對(duì)HTTP請(qǐng)求的二次處理
 


    /// <summary>
    /// 跨域資源訪問(wèn)的HTTP處理程序
    /// </summary>
    public class CorsMessageHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //得到描述目標(biāo)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);
            //根據(jù)HttpActionDescriptor得到應(yīng)用的CorsAttribute特性
            CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault() ??
                controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();
            if (null == corsAttribute)
            {
                return base.SendAsync(request, cancellationToken);
            }
            //利用CorsAttribute實(shí)施授權(quán)并生成響應(yīng)報(bào)頭
            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;
            }

            //添加響應(yīng)報(bào)頭
            if (headers != null && headers.Any())
                foreach (var item in headers)
                    response.Headers.Add(item.Key, item.Value);

            return Task.FromResult<HttpResponseMessage>(response);
        }
    }


2 添加Cors特性,以便處理可以跨域訪問(wèn)的域名,如B網(wǎng)站和C網(wǎng)站
 


    /// <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)
        {

            //設(shè)置響應(yīng)頭"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())
            {
                //設(shè)置響應(yīng)頭"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擴(kuò)展方法
    /// </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();
        }
    }


3 下面是為指定的API類(lèi)型添加指定域名訪問(wèn)的特性
 
    [CorsAttribute("http://localhost:11879/", "http://localhost:5008/")]/*需要加在類(lèi)上*/
    public class ValuesController : ApiController
  {
    //代碼省略
  }
下面看一下實(shí)例的結(jié)果:
 


上圖中分別使用了jsonp和json兩種方法,看一下它們的響應(yīng)結(jié)果
 

 


可以看到,jsonp實(shí)現(xiàn)上是一種遠(yuǎn)程JS方法的調(diào)用,客戶端發(fā)起一個(gè)HTTP請(qǐng)求,這通過(guò)callback參數(shù)(一串隨機(jī)數(shù))來(lái)區(qū)別多個(gè)客戶端,每個(gè)客戶端的請(qǐng)求callback都是不同的,它們由服務(wù)器端處理數(shù)據(jù),再通過(guò)callback隨機(jī)數(shù)去為指定客戶端返回?cái)?shù)據(jù)。
 
 
 
感謝您的閱讀!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多