我的环境:
我遇到了一些无法解决的CORS问题:
使用GET动词启动“简单请求”是可行的,但是使用POST动词时会出现CORS错误,提示:
Access to XMLHttpRequest at 'http://localhost:59837/api/Employees' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
我已经读过mozilla's CORS reference,并且注意到确实我的POST请求首先作为预检请求发送,带有OPTIONS动词和Access-Control-Request-Method: POST
头。
:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
// Web API enable CORS
System.Web.Http.Cors.EnableCorsAttribute cors = new System.Web.Http.Cors.EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
}
,我的POST请求代码如下:
testPOST(){
const credentials = { username: '1', password: '11'};
this.$http.post('http://localhost:59837/api/Employees', {params: {id: '2'}, body: credentials, method: 'POST', headers: {'Content-Type': 'text/plain'}})
.then(function (response){
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
和控制器中的POST功能:
public async Task<HttpResponseMessage> PostEmployees(Credentials creds)
{
Employees emp = await db.Employees.FindAsync(Int32.Parse(creds.username));
if (emp.password.Equals(creds.password))
{
emp.password = "";
return Request.CreateResponse(HttpStatusCode.OK, emp);
}
else
return Request.CreateResponse(HttpStatusCode.Unauthorized, "Username or password are incorrect");
}
我的想法是,也许我需要为授权的“文本/纯文本”定义POST请求标头。使用Fiddler,我发现了传出的请求,但是它没有text / plain标头。 现在,我什至不确定我的错误是否与后端配置或前端请求发送方法有关。 有人遇到过类似的事情吗? (对不起所有代码,我想尽可能少地包含所有内容)
答案 0 :(得分:2)
Web Api 2没有响应OPTIONS请求。您可以为其添加处理程序。这就是我曾经解决它的方式。在某个地方创建一个IHttpModule。这是我的:
namespace AAWebSmartHouse.WebApi.Infrastructure.Modules
{
using System.Web;
public class OPTIONSVerbHandlerModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += (sender, args) =>
{
var app = (HttpApplication)sender;
if (app.Request.HttpMethod == "OPTIONS")
{
app.Response.StatusCode = 200;
app.Response.AddHeader("Access-Control-Allow-Headers", "content-type,accept,authorization");
app.Response.AddHeader("Access-Control-Allow-Origin", "*");
app.Response.AddHeader("Access-Control-Allow-Credentials", "true");
app.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE");
app.Response.AddHeader("Content-Type", "application/json");
app.Response.AddHeader("Accept", "application/json");
app.Response.End();
}
};
}
public void Dispose()
{
}
}
}
并将其添加到Web.config:
<modules>
<!-- ... -->
<add name="OPTIONSVerbHandlerModule" type="AAWebSmartHouse.WebApi.Infrastructure.Modules.OPTIONSVerbHandlerModule" />
</modules>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<!-- ... -->
</handlers>
如果它仍然无法响应控制器中某些方法的OPTIONS请求,我会发现我已经添加了AcceptVerbs属性,如下所示:
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UserInfo")]
[AcceptVerbs("OPTIONS", "GET")]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
return new UserInfoViewModel
{
Email = User.Identity.GetUserName(),
HasRegistered = externalLogin == null,
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
};
}
答案 1 :(得分:0)
解决方案
我认为问题实际上与前端和后端有关。
Microsoft's Web API 2's tutorial似乎没有提到它缺乏对某些客户端作为“飞行前”请求生成的OPTIONS标头的支持。
另外,我在Vue-Resource中使用的参数也引起了一些问题。
后端:
1.我更改了默认行为,该行为导致服务器丢弃 Global.asax.cs 中带有OPTIONS标头的所有请求,谢谢 Obelixx :
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
//Response.Flush();
Response.StatusCode = 200;
Response.AddHeader("Access-Control-Allow-Headers", "content-type,accept,authorization");
Response.AddHeader("Access-Control-Allow-Origin", "*");
Response.AddHeader("Access-Control-Allow-Credentials", "true");
Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE");
Response.AddHeader("Content-Type", "application/json");
Response.AddHeader("Accept", "application/json");
Response.End();
}
我真的不确定这是最好的解决方案,我确信.net程序员完全有理由要删除所有OPTIONS请求,这需要更深入地研究,因此请谨慎使用。
2.如here所示,我还在控制器类中添加了OPTIONS操作:
// OPTIONS: allow pre-flight
public HttpResponseMessage Options()
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
}
前端:
1. vue-resource的API表示对POST请求具有以下签名:
post(url, [body], [config])
但是,您也可以将主体包含在配置参数中。
好吧,没有满足要求。所以代替这个:
this.$http.post('http://localhost:59837/api/Employees',
{params: {id: '2'}, body: credentials, method: 'POST', headers: {'Content-Type': 'text/plain'}})
我这样做了:
this.$http.post('http://localhost:59837/api/Employees', credentials,
{headers: {'Content-Type': 'application/json'}})
请注意我是如何取出正文的:凭证已取出,只是将凭证用作单独的参数。
2.我还将“ Content-Type”标头从 text / plain 更改为 application / json (微软表示,应将 text / plain 类型阻止预检,但这只会导致json格式化程序无法解析凭据)。
----
因此,这就是目前的解决方案,就像我说的是同时具有前端和后端相关的修复程序一样,我不确定是否真的有那么多错误或只是我在许多地方修补的未发现的错误。
我希望这对其他人有帮助。