带有 CORS 和 Angular 的 .NET Web API 2 在第一篇文章中不起作用

Posted

技术标签:

【中文标题】带有 CORS 和 Angular 的 .NET Web API 2 在第一篇文章中不起作用【英文标题】:.NET Web API 2 with CORS and Angular Not Working on First Post 【发布时间】:2017-05-21 23:01:32 【问题描述】:

我正在使用带有 Angular 1 和 Auth0 的 .Net ASP Web API V2。

通过 Auth0 初始登录后,我收到以下错误:

XMLHttpRequest 无法加载 localhost:3001/profile。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,不允许访问 Origin 'localhost:62379'。响应的 HTTP 状态代码为 500。

如果我刷新我不再收到错误。如果我清除浏览器数据,我会再次收到错误,但刷新后会成功。

我在 API 配置中使用所需的 NUGET 包启用了 CORS。

我尝试了以下解决方案,因为我认为这是飞行前的问题:

1) 通过拦截请求在控制器上和全局处理 SELECT 动词。 http://www.jefclaes.be/2012/09/supporting-options-verb-in-aspnet-web.html

2) 将选择动词添加为控制器方法中接受的动词之一。

3) 我在这里找到了各种用于更改请求的答案。 ** 我的代表太低了,我无法分享链接。

等等

4) 使用特定来源而不是 *

控制器:

    [Route("profile")]
    [EnableCors(origins: "*", headers: "*", methods: "OPTIONS")]
    public HttpResponseMessage Options()
    
        var resp = new HttpResponseMessage(HttpStatusCode.OK);
        resp.Headers.Add("Access-Control-Allow-Origin", "*");
        resp.Headers.Add("Access-Control-Allow-Methods", "*");
        return resp;
    


    [Route("profile")]
    [EnableCors(origins: "*", headers: "*", methods: "POST")]
    [HttpPost]
    public object Post([FromBody]dynamic data)
    
        string email = null;
        var b = new object();
        try
        
         **** Logic ****
        
        catch (Exception ex)
        
         **** Return Error ****
        

        return b;
    

API 配置

public static class WebApiConfig

    public static void Register(HttpConfiguration config)
    
        var cors = new EnableCorsAttribute(
            origins: "*",
            headers: "*",
            methods: "*");

        // Web API configuration and services
        config.EnableCors(cors);

        var clientID = WebConfigurationManager.AppSettings["auth0:ClientId"];
        var clientSecret = WebConfigurationManager.AppSettings["auth0:ClientSecret"];

        config.MessageHandlers.Add(new JsonWebTokenValidationHandler()
        
            Audience = clientID,  // client id
            SymmetricKey = clientSecret   // client secret
        );

        // Web API routes
        config.MapHttpAttributeRoutes();

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






角度请求

  function postLogin() 
            var loginPackage = [];
            if (token === null) token = store.get('token');
            if (store.get('profile') != null) 
                loginPackage = store.get('profile');
            

            return $q(function(resolve, reject) 
                $http(
                        method: 'Post',
                        url: BASE + 'profile',
                        data: loginPackage,
                        crossDomain: true,
                        xhrFields: 
                            withCredentials: true
                        ,
                        contentType: 'application/json'
                    )
                    .success(function(data, status) 
                        *** Logic ***
                        resolve(function() 
                            resolve(true);
                        );
                    )
                    .error(function(data, status) 
                            reject(function() 

                            );
                    );
            );

        

我想我可能已经看这个太久了,而忽略了一些相当简单的事情。

////**** 更新****////

回复:

HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcaWFteWFcRGVza3RvcFxOZXcgZm9sZGVyICgzKVx3ZWJhcGlcQXBpXHByb2ZpbGU=?=
X-Powered-By: ASP.NET
Date: Sun, 08 Jan 2017 00:17:26 GMT
Content-Length: 709

//* 更新 *///

使用 clean 浏览器缓存 REQUEST(失败)

POST /profile HTTP/1.1
Host: localhost:3001
Connection: keep-alive
Content-Length: 2372
Accept: application/json, text/plain, */*
Origin: http://localhost:62379
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Authorization: Bearer null
Content-Type: application/json;charset=UTF-8
Referer: http://localhost:62379/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6

我认为这可能是飞行前,但我的理解是使用 OPTIONS 动词而不是 POST 动词。

从外观上看,授权设置不正确。

Authorization: Bearer null

回应:

HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcaWFteWFcRGVza3RvcFxOZXcgZm9sZGVyICgzKVx3ZWJhcGlcQXBpXHByb2ZpbGU=?=
X-Powered-By: ASP.NET
Date: Sun, 08 Jan 2017 00:31:49 GMT
Content-Length: 709

之后立即发送第二个 Post 并且成功。这实际上是飞行前。

请求:

OPTIONS /profile HTTP/1.1
Host: localhost:3001
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:62379
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Access-Control-Request-Headers: authorization, content-type
Accept: */*
Referer: http://localhost:62379/index.html
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6

以及回应:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/10.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: authorization,content-type
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcaWFteWFcRGVza3RvcFxOZXcgZm9sZGVyICgzKVx3ZWJhcGlcQXBpXHByb2ZpbGU=?=
X-Powered-By: ASP.NET
Date: Sun, 08 Jan 2017 00:34:43 GMT
Content-Length: 0

因此,我无法超越登录视图。加载屏幕出现错误。如果我再次刷新并登录,则没有错误。

请求:

POST /profile HTTP/1.1
Host: localhost:3001
Connection: keep-alive
Content-Length: 2367
Accept: application/json, text/plain, */*
Origin: http://localhost:62379
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3ltdC5hdXRoMC5jb20vIiwic3ViIjoiZmFjZWJvb2t8MTAxNTM4MjIxOTc4MTIyNTEiLCJhdWQiOiJWWDhHMFMyUWM5cUFjYnRrM09pMVZMa2NkWGxnWlBtZSIsImV4cCI6MTQ4Mzg3MTMyNywiaWF0IjoxNDgzODM1MzI3fQ.HBQcGC6aad2pLaq3nPuhojrFT2b6Usv64p97b-DCRCU
Content-Type: application/json;charset=UTF-8
Referer: http://localhost:62379/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6

授权字段现在具有令牌而不是空值:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3ltdC5hdXRoMC5jb20vIiwic3ViIjoiZmFjZWJvb2t8MTAxNTM4MjIxOTc4MTIyNTEiLCJhdWQiOiJWWDhHMFMyUWM5cUFjYnRrM09pMVZMa2NkWGxnWlBtZSIsImV4cCI6MTQ4Mzg3MTMyNywiaWF0IjoxNDgzODM1MzI3fQ.HBQcGC6aad2pLaq3nPuhojrFT2b6Usv64p97b-DCRCU

响应成功,登录继续。

令牌似乎只有在刷新后才注入。

我的 AUTH0 配置是:

 authProvider.init(
        domain: A0_DOMAIN,
        clientID: A0_CLIENT,
        callbackUrl: location.href,
        loginState: 'out.login',
        options:options
    );



    var refreshingToken = null;
    jwtInterceptorProvider.tokenGetter = function(store, jwtHelper) 
        var token = store.get('token');
        var refreshToken = store.get('refreshToken');
        if (token) 
            if (!jwtHelper.isTokenExpired(token)) 
                return store.get('token');
             else 
                if (refreshingToken === null) 
                    refreshingToken = auth.refreshIdToken(refreshToken)
                        .then(function(idToken) 
                            store.set('token', idToken);
                            return idToken;
                        )
                        .finally(function() 
                            refreshingToken = null;
                        );
                
            
        

    ;

    jwtOptionsProvider.config(
        whiteListedDomains: ['localhost','https://www.ymtechno.com','https://www.ymtechno.com/_api','https://ymtechno.com/_api']
    );

    $httpProvider.interceptors.push('jwtInterceptor');

)
.run(function($rootScope, auth, store, jwtHelper, $location) 
    var refreshingToken = null;
    $rootScope.$on('$locationChangeStart',
        function() 
            var token = store.get('token');
            var refreshToken = store.get('refreshToken');
            if (token) 
                if (!jwtHelper.isTokenExpired(token)) 
                    auth.authenticate(store.get('profile'), token);
                 else 
                    if (refreshingToken === null) 
                        refreshingToken = auth.refreshIdToken(refreshToken)
                            .then(function(idToken) 
                                store.set('token', idToken);
                                return idToken;
                            )
                            .finally(function() 
                                refreshingToken = null;
                            );
                        return refreshingToken;
                     else 
                        $location.path('login');
                    
                
            

        );

    auth.hookEvents();

【问题讨论】:

我不会编程.net,但让我印象深刻的是你有[AcceptVerbs("OPTIONS")],但你的EnableCors 说允许* 方法。您的 Web 框架似乎覆盖了您尝试手动添加的标头。您应该让您的框架定义这些标头。检查您的注释。 我认为这是正确的,但是在清除我的缓存后,我又遇到了同样的错误。一旦我得到第一个错误,我就可以登录没有问题。 删除这个[AcceptVerbs("OPTIONS")] 请提供原始 http 响应 现在包含响应。 【参考方案1】:

经过调查,我发现拦截器在重新启动应用程序之前没有将令牌放入 auth 标头中。这被读取为 CORS 错误,因为请求不符合访问 API 的条件。令牌值未定义。

可以在此处找到有关注入器和标头的更深入的线程:

Set HTTP header for one request

如果您遇到相同的问题,这是一个有用的参考。

【讨论】:

以上是关于带有 CORS 和 Angular 的 .NET Web API 2 在第一篇文章中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Angular 7 .net 核心 2.2 WebApi Windows 身份验证 CORS

.net mvc angular应用程序中的CORS问题[关闭]

在带有 Angular 的 Spring Boot 中启用了 Cors,但仍然出现 cors 错误

ASP.NET Core:带有 OPTIONS 异常的 Windows 身份验证(CORS 预检)

带有 Keycloak 的 Angular 和 Spring Boot REST API 的 CORS 问题

带有 Keycloak 的 Angular 和 Spring Boot REST API 的 CORS 问题