• asp.net web api 跨域问题


     

     

    缘起

    以前在asp.net mvc时代,很少出现跨域问题

    自从使用了asp.net web api + angular (1/2)之后,开始有跨域问题了。

    简单普及下跨域:

    我的理解是只要是前台页面与后台服务不在同一域名下,或者同域名不同端口下,就是跨域,我做个真值表:

    前端 后端 是否跨域
    http://localhost:3000 http://localhost:3000  
    http://localhost:3000 http://localhost:2000
    http://localhost:3000 http://localhost:5000

     

     

     

     

     

     如果跨域,最直接的表现就如下图:

    下面我帖上测试代码(未解决跨域问题之前):

    前端:

    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {       
    
            $("#btn2").click(function () {
                $.ajax({
                    type: "POST",
                    url: "http://localhost:4272/api/Resources/GetPrintMethod",                
                    success: function (data) {
                        console.log(data);
                    },
                    error: function (e, XMLHttpRequest, textStatus) {
                        console.log(e);
                    },                
                });
            });
        });
    
    
        $(function () {
            //.ajaxError事件定位到document对象,文档内所有元素发生ajax请求异常,都将冒泡到document对象的ajaxError事件执行处理,ajax方法中有error,先处理error,再冒泡到此处的error
            $(document).ajaxError(
    
                //所有ajax请求异常的统一处理函数,处理
                function (event, xhr, options, exc) {
                    alert("StatusCode:" + xhr.status);
                    if (xhr.status == 'undefined') {
                        return;
                    }
                    switch (xhr.status) {
                        case 403:
                            // 未授权异常
                            alert("系统拒绝:您没有访问权限。");
                            break;
    
                        case 404:
                            alert("您访问的资源不存在。");
                            break;
                    }
                }
            );
        });
    </script>
    
    <body>        
        <input type="button" value="测试跨域请求" id="btn2">
    </body>
    View Code

    后端就是一个asp.net web api程序,我就不全贴了,只挑重点了:

    目前只有一个Controller:

    using System.Collections.Generic;
    using System.Threading.Tasks;
    using System.Web.Http;
    
    namespace Summer.Api.Controllers
    {
        [RoutePrefix("api/Resources")]
        public class ResourcesController : ApiController
        {
            
            [Route("GetPrintMethod")]
            [HttpPost]
            [HttpGet]
            public async Task<IHttpActionResult> GetPrintMethod()
            {
                var list = new List<string>
                {
                    "数据1",
                    "数据2"
                };
    
                return Ok(new { frontPrintMethod = list });
            }
        }
    }
    View Code

    这时,我们运行起来后端,在Controller上打上断点:

    然后前端请求:

    首先,Controller断点生效,似乎正常通过,return Ok(.....),

    But,前台却得到:

    这是前端的一个异常捕获,status code是0,0是什么?这显然不是我们想要的

    在 https://blog.csdn.net/zheng963/article/details/42237709 我查到了一些端倪:

    竟然还没有调用Send方法?这是什么鬼?WTF...为什么会这样呢?

    再观察下NetWork看看:

    发现有1次请求,并且状态是200,照理说应当没问题了,可为什么会得到statuscode 0呢?

    Console还有一行错误信息:

    Failed to load http://localhost:4272/api/Resources/GetPrintMethod: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

    先看一下详细信息再说:

    200,OK,竟然还得到了数据,不错,不错,可是为什么有那行错误信息呢?

    那行错误信息

    Failed to load http://localhost:4272/api/Resources/GetPrintMethod: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

    这段是说被请求的资源没有允许跨域,所以不允许访问,可是奇怪,不允许访问,为什么还能得到结果呢。。。奇怪。。。不知道是不是Bug。。。。。

    尝试解决

    下面我来尝试解决它

    既然是跨域,那就找解决方案呗,后台是WebApi,NuGet查找并安装 Microsoft.AspNet.WebApi.Cors

    然后在WebApiConfig中加上一句:

    config.EnableCors(new System.Web.Http.Cors.EnableCorsAttribute("*", "*", "*"));

    再次运行请求看下,这次我们成功得到了结果,并没有报错对比下加没加跨域插件下的响应报文头:

    我发现加了跨域插件后,响应报文头,自动加了一行:

    Access-Control-Allow-Origin: *
    有了它,就允许跨域了,问题似乎得到了解决。
     
     

    弦外

    人就是不满足,现在,我要加需求,我Api现在有了一个要求:

    现在我允许了跨域,你爱是哪来哪来,我都允许,但是。。。

    (据说但是之前的都是XXX)

    你必须要有合法的Token,Token是什么,如何获取,如何验证它是否合法,这里不介绍。

    只是我要有一个Token,随便是什么,然后,API来模拟在Action执行前拦截进行Token校验。

    先写一个 MyAuthHandler继承自 DelegatingHandler,具体代码如下:

     1 using System;
     2 using System.Net;
     3 using System.Net.Http;
     4 using System.Threading;
     5 using System.Threading.Tasks;
     6 using System.Web;
     7 
     8 namespace Summer.Api.Providers
     9 {
    10     public class MyAuthHandler : DelegatingHandler
    11     {
    12         protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
    13             CancellationToken cancellationToken)
    14         {
    15             var authHeader = request.Headers.Authorization;
    16 
    17             try
    18             {
    19                 var token = authHeader.Scheme;
    20                 if (string.IsNullOrWhiteSpace(token))
    21                     return request.CreateErrorResponse(HttpStatusCode.Forbidden, "invalid_grant. Token is empty.");
    22             }
    23 
    24             catch (Exception ex)
    25             {
    26                 return request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message, ex);
    27             }
    28 
    29             return await base.SendAsync(request, cancellationToken);
    30         }
    31 }
    View Code

     然后把它加也加在WebApiConfig中:

     在MyAuthHandler中我重写了SendAsync方法,这样每当服务器在执行真正Action之前都会先走这个方法,我就可以在这里进行请求合法性检测了。

    运行前端再次请求,依然得到了StatusCode 0,和500错误,并且,跨域的问题又回来了:

    好难缠啊,又跨域,似乎那插件没有对新加的Handler起作用。

    分析下:

    500,是因为请求Headers中没有Authorization,更加没有Token,所以我们直接返回

     return request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message, ex);

    这个OK

    但为什么我没有得到这500,而却得到了0?

    观察下响应头:

    呃,问题原来在这里,响应头中又没有了:

    Access-Control-Allow-Origin: *

    好吧,插件不起作用,我手动加上总可以了吧

    Handler中改一下:

    再次调用查看结果:

    Excellent, That's what I really needed.

    终于得到了我想要的正确的Code问题完美解决。

    尾声

    回顾下过程:

    1.出现跨域

    2.引用Cros插件解决跨域

    3.新增需求,要在Action执行前进行Token验证

    4.新增MyAuthHandler : DelegatingHandler来在每一次执行Action前进行拦截检测

    5.拦截后又出现跨域,Cros插件未对其起作用

    6.手动为Response添加允许跨域响应头:

    Access-Control-Allow-Origin: *

    7.问题遭到解决

    But:

    为什么在没有加允许跨域时,虽然得到错误,却也得到了数据呢?

    Angular

    再次使用Angular2来调用WebApi

    依然可以得到正确Code,跨域问题完美解决在 Asp.net Web Api

    .net Core中似乎不存在这么复杂,没有继续测试

    node里,其实更加简单,只要在每次返回时加上允许跨域头即可。 

  • 相关阅读:
    Hibernate中的HQL
    hibernate配置数据库连接池三种用法
    Hibernate的延迟检索和立即检索
    Hibernate关系映射中的注解
    Hibernate的多种关系映射(oto、otm、mtm)
    自然主键和代理主键的区别
    Hibernate的xml方法配置和操作代码
    Hibernate简介
    VirtualBox从USB设备(PE)启动图文教程
    属性动画
  • 原文地址:https://www.cnblogs.com/avictor/p/9406613.html
Copyright © 2020-2023  润新知