ASP.NET WebApi 在 ValidateClientAuthentication 上回答 400 对 OPTIONS 的错误请求,即使在 context.Validated()
ASP.NET WebApi Answer 400 Bad Request to OPTIONS on ValidateClientAuthentication, even on context.Validated()
我有一个 WebApi 项目的 angularjs HTML 客户端。当我通过 POSTMAN 或其他 REST Client 测试 API 时,似乎一切正常。
当我开始将浏览器与我的 angularjs 客户端一起使用时,浏览器总是使用 OPTIONS 启动预检请求。在那里,我的 WebAPI 总是回答 400 Bad Request - 我仍然处于“/api/token”阶段。
我已经将我的 WebAPI 项目的每个点附加到调试器。我还根据 SO 中关于如何启用 CORS 的几个答案更改了几点。其中一些我已经尝试过:更改 web.config 以添加 headers 在每个请求上启用 cors,将 cors 添加到 WebApi 启动,在“/token”重写函数中启用 cors。
这是我得到的:
Angularjs TypeScript 调用“/api/token”:
logIn = (userName: string, userPassword: string): ng.IPromise<void> => {
var postData = {
"grant_type": "password",
"client_id": this.appConfiguration.ClientId,
"client_secret": this.appConfiguration.ClientSecret,
"username": userName,
"password": userPassword
};
return this.$http.post<models.LoggedUserModel>('http://local.web.api/api/token', $.param(postData), {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then((result) => {
this.localStorageService.set('Auth', result);
this.goHome(true);
}).catch((error) => {
console.warn(error);
});
}
这是在我的 WebApi 上调用的唯一函数:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// handle cors requests
if (!string.IsNullOrEmpty(context.OwinContext.Request.Headers.Get("Origin")))
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new string[] { "*" });
}
try
{
// retrieve clientId and clientSecret from request body
string clientId;
string clientSecret;
if (context.TryGetFormCredentials(out clientId, out clientSecret))
{
// here it comes our application specific security code....
}
else
{
// this is part of enabling CORS..
if (context.Request.Method.ToUpper() == "OPTIONS")
{
// it returns OK to preflight requests having an empty body
context.Validated();
}
}
}
finally
{
// log stuff...
}
}
如果我只是离开 OWIN Cors 的东西,添加 headers 并调用“context.Validated()”,一切都将继续相同。这是我得到的:
Firefox Network Tab:
--------------------
Request URL: http://local.web.api/api/token
Request method: OPTIONS
Remote address: 127.0.0.1:80
Status code: 400 Bad Request
Version: HTTP/1.1
Request headers:
----------------
Host: local.web.api
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.7,pt-BR;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Origin: http://local.web.client
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Response headers:
-----------------
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Length: 34
Content-Type: application/json;charset=UTF-8
Date: Tue, 22 Dec 2015 15:24:23 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
我真的很感激关于去哪里的一些想法。
这对我来说是全新的,我也操作过一些其他的 WebApi 项目 + angularjs.
好的,这太糟糕了,但我发现了问题所在。
我在 angularjs 上使用一个 http 拦截器,它会自动检查登录的用户,并在需要时添加带有 Bearer
令牌的 Authorization
header。问题是我做错了。
我在 config
object 中创建了一个新的 属性 作为布尔值,这将是添加或不添加 Authorization
的标志header。删除它实际上修复了代码。不知道为什么,但现在分析请求我可以看到所有 headers 实际上都按预期发送:Content-Type
在第一个案例中没有被正确填写。奇怪的是 angularjs.
没有发出警告
// http auth interceptor
angularApplication.factory('httpAuthInterceptor', ['$rootScope', '$injector', '$location', ($rootScope, $injector, $location): ng.IHttpInterceptor => {
var $q: ng.IQService = $injector.get('$q');
var localStorageService: ng.local.storage.ILocalStorageService = $injector.get('localStorageService');
return {
request: (config: ng.IRequestConfig): ng.IRequestConfig => {
// check if headers are present
config.headers = config.headers || {};
// the error was here! I was trying to add properties to config that I think angular was not expecting
// removing this line solved the issue
// if (!config.bypassToken) {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if (loggedUserInfo) {
config.headers['Authorization'] = 'Bearer ' + loggedUserInfo.access_token;
}
return config;
},
responseError: (rejection) => {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if ((rejection.status === 401) && (loggedUserInfo)) {
// if so, then the user must login againd
localStorageService.remove('Auth');
$location.path('/home');
console.error(rejection);
}
return $q.reject(rejection);
}
};
}]);
感谢您的帮助。
我只是在这里发布这个以防有人遇到类似的问题。
不要乱用 config
object!
我有一个 WebApi 项目的 angularjs HTML 客户端。当我通过 POSTMAN 或其他 REST Client 测试 API 时,似乎一切正常。
当我开始将浏览器与我的 angularjs 客户端一起使用时,浏览器总是使用 OPTIONS 启动预检请求。在那里,我的 WebAPI 总是回答 400 Bad Request - 我仍然处于“/api/token”阶段。
我已经将我的 WebAPI 项目的每个点附加到调试器。我还根据 SO 中关于如何启用 CORS 的几个答案更改了几点。其中一些我已经尝试过:更改 web.config 以添加 headers 在每个请求上启用 cors,将 cors 添加到 WebApi 启动,在“/token”重写函数中启用 cors。
这是我得到的:
Angularjs TypeScript 调用“/api/token”:
logIn = (userName: string, userPassword: string): ng.IPromise<void> => {
var postData = {
"grant_type": "password",
"client_id": this.appConfiguration.ClientId,
"client_secret": this.appConfiguration.ClientSecret,
"username": userName,
"password": userPassword
};
return this.$http.post<models.LoggedUserModel>('http://local.web.api/api/token', $.param(postData), {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then((result) => {
this.localStorageService.set('Auth', result);
this.goHome(true);
}).catch((error) => {
console.warn(error);
});
}
这是在我的 WebApi 上调用的唯一函数:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// handle cors requests
if (!string.IsNullOrEmpty(context.OwinContext.Request.Headers.Get("Origin")))
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new string[] { "*" });
}
try
{
// retrieve clientId and clientSecret from request body
string clientId;
string clientSecret;
if (context.TryGetFormCredentials(out clientId, out clientSecret))
{
// here it comes our application specific security code....
}
else
{
// this is part of enabling CORS..
if (context.Request.Method.ToUpper() == "OPTIONS")
{
// it returns OK to preflight requests having an empty body
context.Validated();
}
}
}
finally
{
// log stuff...
}
}
如果我只是离开 OWIN Cors 的东西,添加 headers 并调用“context.Validated()”,一切都将继续相同。这是我得到的:
Firefox Network Tab:
--------------------
Request URL: http://local.web.api/api/token
Request method: OPTIONS
Remote address: 127.0.0.1:80
Status code: 400 Bad Request
Version: HTTP/1.1
Request headers:
----------------
Host: local.web.api
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.7,pt-BR;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Origin: http://local.web.client
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Response headers:
-----------------
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Length: 34
Content-Type: application/json;charset=UTF-8
Date: Tue, 22 Dec 2015 15:24:23 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
我真的很感激关于去哪里的一些想法。 这对我来说是全新的,我也操作过一些其他的 WebApi 项目 + angularjs.
好的,这太糟糕了,但我发现了问题所在。
我在 angularjs 上使用一个 http 拦截器,它会自动检查登录的用户,并在需要时添加带有 Bearer
令牌的 Authorization
header。问题是我做错了。
我在 config
object 中创建了一个新的 属性 作为布尔值,这将是添加或不添加 Authorization
的标志header。删除它实际上修复了代码。不知道为什么,但现在分析请求我可以看到所有 headers 实际上都按预期发送:Content-Type
在第一个案例中没有被正确填写。奇怪的是 angularjs.
// http auth interceptor
angularApplication.factory('httpAuthInterceptor', ['$rootScope', '$injector', '$location', ($rootScope, $injector, $location): ng.IHttpInterceptor => {
var $q: ng.IQService = $injector.get('$q');
var localStorageService: ng.local.storage.ILocalStorageService = $injector.get('localStorageService');
return {
request: (config: ng.IRequestConfig): ng.IRequestConfig => {
// check if headers are present
config.headers = config.headers || {};
// the error was here! I was trying to add properties to config that I think angular was not expecting
// removing this line solved the issue
// if (!config.bypassToken) {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if (loggedUserInfo) {
config.headers['Authorization'] = 'Bearer ' + loggedUserInfo.access_token;
}
return config;
},
responseError: (rejection) => {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if ((rejection.status === 401) && (loggedUserInfo)) {
// if so, then the user must login againd
localStorageService.remove('Auth');
$location.path('/home');
console.error(rejection);
}
return $q.reject(rejection);
}
};
}]);
感谢您的帮助。
我只是在这里发布这个以防有人遇到类似的问题。
不要乱用 config
object!