CORS-Ajax错误函数将返回401的API请求的错误代码报告为0 背景
当身份验证(JWT)成功时,我让COR在本地开发环境中工作。我让客户端页面运行localhost并调用api.mycompany.com获取数据。 我的api项目检查有效的JWT,如果通过,则返回内容。我花了一段时间才来到这里,但一切都很好 如果我没有发送有效的JWT,api将正确响应401(在Fiddler中选中),但客户端上的错误函数回调将报告错误代码0和状态“error” 我希望ajax回调函数检查错误的状态代码,如果是401,则检查标题中名为location的标题(其中将包含认证服务的uri) 安装程序CORS-Ajax错误函数将返回401的API请求的错误代码报告为0 背景,ajax,jquery,cors,Ajax,Jquery,Cors,当身份验证(JWT)成功时,我让COR在本地开发环境中工作。我让客户端页面运行localhost并调用api.mycompany.com获取数据。 我的api项目检查有效的JWT,如果通过,则返回内容。我花了一段时间才来到这里,但一切都很好 如果我没有发送有效的JWT,api将正确响应401(在Fiddler中选中),但客户端上的错误函数回调将报告错误代码0和状态“error” 我希望ajax回调函数检查错误的状态代码,如果是401,则检查标题中名为location的标题(其中将包含认证服务的u
- (API项目)在本地IIS Express上运行MVC4项目的Visual Studio 2012实例
- 本地主机文件将127.0.0.1映射到api.mycompany.com
- 将Project->Properties->Web设置为IIS Express
- 使用本地IIS Express(选中)
- 项目Url:
http://localhost:8080
- 创建虚拟目录
- 覆盖应用程序根URL(已选中)
- 覆盖应用程序根URL:
http://api.mycompany.com:8080
- 在“站点”下的applicationhost.config中:
物料控制器<site name="StuffManagerAPI" id="1"> <application path="/" applicationPool="Clr4IntegratedAppPool"> <virtualDirectory path="/" physicalPath="C:\Users\me\Documents\Visual Studio 2012\Projects\StuffManagerAPI\StuffManagerAPI" /> </application> <bindings> <binding protocol="http" bindingInformation="*:8080:localhost" /> <binding protocol="http" bindingInformation="*:8080:api.mycompany.com" /> </bindings> </site>
using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace StuffManagerAPI.Handlers { public class AuthorizationHeaderHandler : DelegatingHandler { private const string KEY = "theKey"; protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>(); const string identityProviderUri = "https://idp.mycompany.com"; IEnumerable<string> apiKeyHeaderValues = null; if (request.Headers.TryGetValues("Authorization", out apiKeyHeaderValues)) { var apiKeyHeaderValue = apiKeyHeaderValues.First(); var token = apiKeyHeaderValue.Split(' ').LastOrDefault(); var tokenProcessor = new JasonWebTokenDecryptor.JasonWebToken(token, KEY); if (tokenProcessor.IsValid) { base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result)); } else { var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri); taskCompletionSource.SetResult(response); } } else { if(request.Method.Method != "OPTIONS") { //No Authorization Header therefore needs to redirect var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri); taskCompletionSource.SetResult(response); } else { base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result)); } } return taskCompletionSource.Task; } private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri) { // Create the response. var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add("Location", identityProviderUri); return response; } } }
using System.Collections.Generic; using System.Linq; using System.Net; using System.Web.Http; using StuffManagerAPI.Attributes; using StuffManagerAPI.Models; namespace StuffManagerAPI.Controllers { [HttpHeader("Access-Control-Allow-Origin", "*")] [HttpHeader("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE")] [HttpHeader("Access-Control-Allow-Headers", "Authorization")] [HttpHeader("Access-Control-Expose-Headers", "Location")] public class StuffController : ApiController { private readonly Stuff[] _stuff = new[] { new Stuff { Id = "123456", SerialNumber = "112233", Description = "Cool Item" }, new Stuff { Id = "456789", SerialNumber = "445566", Description = "Another Cool Item" } }; public Stuff Get(string id) { var item = _stuff.FirstOrDefault(p => p.Id == id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; } public IEnumerable<Stuff> GetAll() { return _stuff; } public void Options() { // nothing.... } } }
客户项目 main.html使用System.Collections.Generic; 使用System.Linq; Net系统; 使用System.Web.Http; 使用StuffManagerAPI.Attributes; 使用StuffManagerAPI.Models; 命名空间StuffManagerAPI.Controllers { [HttpHeader(“访问控制允许来源”,“*”)] [HttpHeader(“访问控制允许方法”、“选项、HEAD、GET、POST、PUT、DELETE”)] [HttpHeader(“访问控制允许标头”、“授权”)] [HttpHeader(“访问控制公开头”、“位置”)] 公共类StuffController:ApicController { 私有只读内容[]_Stuff=new[] { 新东西 { Id=“123456”, SerialNumber=“112233”, Description=“酷项” }, 新东西 { Id=“456789”, SerialNumber=“445566”, Description=“另一款酷款” } }; 公共内容获取(字符串id) { var item=\u stuff.FirstOrDefault(p=>p.Id==Id); 如果(项==null) { 抛出新的HttpResponseException(HttpStatusCode.NotFound); } 退货项目; } 公共IEnumerable GetAll() { 退货; } 公开作废期权() { //没什么。。。。 } } }
<!DOCTYPE html> <html lang="en"> <head> <title>ASP.NET Web API</title> <link href="../Content/Site.css" rel="stylesheet" /> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.js"></script> <script type="text/javascript"> var baseUrl = "http://api.mycompany.com:8080/api/"; $.support.cors = true; $(document).ready( getListofStuff() ); function setHeader(xhr) { xhr.setRequestHeader('authorization', 'Bearer blah.blah.blah'); } function getListofStuff() { var apiUrl = baseUrl + "stuff/"; $.ajax({ url: apiUrl, type: 'GET', dataType: 'json', crossDomain: true, success: receivedListOfStuff, error: receiveError, beforeSend: setHeader, statusCode: { 0: function() { alert('got 0'); }, 401: function () { alert('finally got a 401'); } } }); } function getIndividualPieceOfStuff(id) { var apiUrl = baseUrl + "stuff/" + id; $.ajax({ url: apiUrl, type: 'GET', dataType: 'json', crossDomain: true, success: receivedIndividualStuffItem, error: receiveError, beforeSend: setHeader }); } function receivedListOfStuff(data) { $.each(data, function (key, val) { var listItem = $('<li/>').text(val.Description); listItem.data("content", { id: val.Id}); $(".myStuff").prepend(listItem); }); $(".myStuff li").click(function () { getIndividualPieceOfStuff($(this).data("content").id); }); } function receivedIndividualStuffItem(data) { $("#stuffDetails #id").val(data.Id); $("#stuffDetails #serialNumber").val(data.SerialNumber); $("#stuffDetails #description").val(data.Description); } function receiveError(xhr, textStatus, errorThrown) { var x = xhr.getResponseHeader("Location"); var z = xhr.responseText; if (xhr.status == 401){ alert('finally got a 401'); } alert('Error AJAX'); } </script> </head> <body> . . . . </body> </html>
而是直接在ajax上检查状态代码,您是否可以使用此代码在onComplete上检查水果刀 var baseUrl=”http://api.mycompany.com:8080/api/"; $.support.cors=true; $(文件)。准备好了吗( getListofStuff() ); 函数setHeader(xhr){ setRequestHeader('authorization','beareh.blah.blah'); } 函数getListofStuff(){ var apiUrl=baseUrl+“stuff/”; $.ajax({ url:apiUrl, 键入:“GET”, 数据类型:“json”, 跨域:是的, 成功:已收到Istofstuff, 错误:receiveError, 发送前:setHeader, 状态代码:{ 0:函数(){ 警报('got0'); }, 401:函数(){ 警惕(“终于拿到了401”); } } }); } 函数getIndividualPieceOfStuff(id){ var apiUrl=baseUrl+“stuff/”+id; $.ajax({ url:apiUrl, 键入:“GET”, 数据类型:“json”, 跨域:是的, 成功:接受分个人的训练, 错误:receiveError, 发送前:setHeader }); } 函数receivedListOfStuff(数据){ $。每个(数据、函数(键、值){ var listItem=$(“”).text(val.Description); 数据(“内容”{id:val.id}); $(“.myStuff”).prepend(列表项); }); $(“.myStuff li”)。单击(函数(){ getIndividualPieceofStuf($(this).data(“content”).id); }); } 函数receivedIndividualStuffItem(数据){ $(“#stuffDetails#id”).val(data.id); $(“#stuffDetails#serialNumber”).val(data.serialNumber); $(“#stuffDetails#description”).val(data.description); } 函数receiveError(xhr、textStatus、ErrorSwitch){ var x=xhr.getResponseHeader(“位置”); var z=xhr.responseText; 如果(xhr.status==401){ 警惕(“终于拿到了401”); } 警报('Error AJAX'); } . . . .
> $.ajaxSetup({ > error: function (x) { > if (x.status == 401) { > alert("401"); > } > } });
我终于明白了。在授权标头处理程序中,当tokenProcessor.IsValid为false时,我跳转到FailedResponseWithAddressToIdentityProvider,然后立即设置结果并将任务标记为完成。因此,我从不访问Stuff Controller并添加访问控制标题:
} 也许有更好的方法可以做到这一点,但我只是在FailedResponseWithAddressToIdentityProvider方法中将标题添加到我的响应中,浏览器最终在Chrome、Firefox和IE8中看到401。变化如下:if (tokenProcessor.IsValid) { base.SendAsync(request, cancellationToken).ContinueWith(t => taskCompletionSource.SetResult(t.Result)); } else { var response = FailedResponseWithAddressToIdentityProvider(identityProviderUri); taskCompletionSource.SetResult(response); } . . . private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri) { // Create the response. var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add("Location", identityProviderUri); return response; }
private static HttpResponseMessage FailedResponseWithAddressToIdentityProvider(string identityProviderUri) { // Create the response. var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add("Location", identityProviderUri); response.Headers.Add("Access-Control-Allow-Origin", "*"); response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE"); response.Headers.Add("Access-Control-Allow-Headers", "Authorization"); response.Headers.Add("Access-Control-Expose-Headers", "Location"); return response; }
我想我也应该在其他浏览器中测试…Firefox返回0,IE 8得到401。OddI对IE8的反应感到惊讶。但一般来说,大多数浏览器给用户提供的信息很少