JQuery 中的 HttpClient PostAsync 等效于 FormURLEncodedContent 而不是 JSON
HttpClient PostAsync equivalent in JQuery with FormURLEncodedContent instead of JSON
我编写了一个 JQuery 脚本来执行用户登录 POST(尝试执行我在附加信息部分使用 C# 执行的操作,见下文)。
使用 html 页面中的 JQuery 代码触发 POST 后,我发现了以下问题:
1 - 我调试了服务器端代码,我知道 POST 被服务器接收(在 ValidateClientAuthentication() 函数,但不在 GrantResourceOwnerCredentials() 函数中)。
2 - 此外,在服务器端,我找不到 username 和 password,应该用 postdata 发布。然而,使用 用户端 C# 代码 ,当我调试到 服务器端 C# 代码时,我可以在 context 变量中看到这些值。我认为,这就是问题的全部根源。
3 - JQuery 代码调用 函数 getFail()。
? - 我想知道,这个 JQuery 代码 与 有什么不同下面的 C# 用户端代码,我该如何修复它,让它们做同样的工作?
(我的猜测: 是 JSON.stringify 和 FormURLEncodedContent 做一些不同的事情)
JQuery/Javascript代码:
function logIn() {
var postdata = JSON.stringify(
{
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
});
try {
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: postdata,
dataType: "json",
success: getSuccess,
error: getFail
});
} catch (e) {
alert('Error in logIn');
alert(e);
}
function getSuccess(data, textStatus, jqXHR) {
alert('getSuccess in logIn');
alert(data.Response);
};
function getFail(jqXHR, textStatus, errorThrown) {
alert('getFail in logIn');
alert(jqXHR.status); // prints 0
alert(textStatus); // prints error
alert(errorThrown); // prints empty
};
};
服务器端处理POST (C#):
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
// after this line, GrantResourceOwnerCredentials should be called, but it is not.
await Task.FromResult(context.Validated());
}
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await manager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
// Add claims associated with this user to the ClaimsIdentity object:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
foreach (var userClaim in user.Claims)
{
identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
}
context.Validated(identity);
}
附加信息: 在我的 C# Owin Web 服务器的 C# 客户端测试应用程序中,我需要执行以下代码POST(工作正常):
用户端POST (C#):
//...
HttpResponseMessage response;
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>( "grant_type", "password"),
new KeyValuePair<string, string>( "username", userName ),
new KeyValuePair<string, string> ( "password", password )
};
var content = new FormUrlEncodedContent(pairs);
using (var client = new HttpClient())
{
var tokenEndpoint = new Uri(new Uri(_hostUri), "Token"); //_hostUri = http://localhost:8080/Token
response = await client.PostAsync(tokenEndpoint, content);
}
//...
不幸的是,dataType
控制 jQuery 期望 返回的 数据是什么,而不是 data
是什么。要设置 request 数据 (data
) 的内容类型,请改用 contentType: "json"
。 (更多内容在 the documentation。)
var postdata = JSON.stringify(
{
"username": document.getElementById("username").value,
"password": document.getElementById("password").值
});
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: postdata,
dataType: "json",
contentType: "json", // <=== Added
success: getSuccess,
error: getFail
});
如果您不是要 发送 JSON,而是想发送通常的 URI 编码表单数据,则不会使用 JSON.stringify
并且只会将对象直接交给 jQuery 的 ajax
; jQuery 然后将创建 URI 编码的表单。
try {
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: {
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
},
dataType: "json",
success: getSuccess,
error: getFail
});
// ...
补充一下 T.J. 的回答,将 JSON 发送到 /token
端点不起作用的另一个原因是它不支持 JSON.
即使您将 $.ajax's
contentType 选项设置为 application/json
,就像您将 JSON 数据发送到 MVC 或 Web API 一样,/token
也赢了'接受那个有效载荷。它只支持形式 URL 编码对(例如 username=dave&password=hunter2)。如果你将一个对象传递给它的 data
选项,$.ajax
会自动为你编码,就像你的 postdata
变量,如果它没有被 JSON 字符串化的话。
此外,您必须记住在您的请求中包含 grant_type=password
参数(就像您的 PostAsync()
代码一样)。否则,/token
端点将响应 "invalid grant type" 错误,即使用户名和密码实际上是正确的。
发送表单数据时应使用jquery的$.param对数据进行urlencode。 AngularJs' $http 方法目前不执行此操作。
喜欢
var loginData = {
grant_type: 'password',
username: $scope.loginForm.email,
password: $scope.loginForm.password
};
$auth.submitLogin($.param(loginData))
.then(function (resp) {
alert("Login Success"); // handle success response
})
.catch(function (resp) {
alert("Login Failed"); // handle error response
});
从 angularjs 1.4 开始,这对于 $httpParamSerializerJQLike:
来说非常简单
.controller('myCtrl', function($http, $httpParamSerializerJQLike) {
$http({
method: 'POST',
url: baseUrl,
data: $httpParamSerializerJQLike({
"user":{
"email":"wahxxx@gmail.com",
"password":"123456"
}
}),
headers:
'Content-Type': 'application/x-www-form-urlencoded'
})
})
我编写了一个 JQuery 脚本来执行用户登录 POST(尝试执行我在附加信息部分使用 C# 执行的操作,见下文)。
使用 html 页面中的 JQuery 代码触发 POST 后,我发现了以下问题:
1 - 我调试了服务器端代码,我知道 POST 被服务器接收(在 ValidateClientAuthentication() 函数,但不在 GrantResourceOwnerCredentials() 函数中)。
2 - 此外,在服务器端,我找不到 username 和 password,应该用 postdata 发布。然而,使用 用户端 C# 代码 ,当我调试到 服务器端 C# 代码时,我可以在 context 变量中看到这些值。我认为,这就是问题的全部根源。
3 - JQuery 代码调用 函数 getFail()。
? - 我想知道,这个 JQuery 代码 与 有什么不同下面的 C# 用户端代码,我该如何修复它,让它们做同样的工作?
(我的猜测: 是 JSON.stringify 和 FormURLEncodedContent 做一些不同的事情)
JQuery/Javascript代码:
function logIn() {
var postdata = JSON.stringify(
{
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
});
try {
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: postdata,
dataType: "json",
success: getSuccess,
error: getFail
});
} catch (e) {
alert('Error in logIn');
alert(e);
}
function getSuccess(data, textStatus, jqXHR) {
alert('getSuccess in logIn');
alert(data.Response);
};
function getFail(jqXHR, textStatus, errorThrown) {
alert('getFail in logIn');
alert(jqXHR.status); // prints 0
alert(textStatus); // prints error
alert(errorThrown); // prints empty
};
};
服务器端处理POST (C#):
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
// after this line, GrantResourceOwnerCredentials should be called, but it is not.
await Task.FromResult(context.Validated());
}
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await manager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
// Add claims associated with this user to the ClaimsIdentity object:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
foreach (var userClaim in user.Claims)
{
identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
}
context.Validated(identity);
}
附加信息: 在我的 C# Owin Web 服务器的 C# 客户端测试应用程序中,我需要执行以下代码POST(工作正常):
用户端POST (C#):
//...
HttpResponseMessage response;
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>( "grant_type", "password"),
new KeyValuePair<string, string>( "username", userName ),
new KeyValuePair<string, string> ( "password", password )
};
var content = new FormUrlEncodedContent(pairs);
using (var client = new HttpClient())
{
var tokenEndpoint = new Uri(new Uri(_hostUri), "Token"); //_hostUri = http://localhost:8080/Token
response = await client.PostAsync(tokenEndpoint, content);
}
//...
不幸的是,dataType
控制 jQuery 期望 返回的 数据是什么,而不是 data
是什么。要设置 request 数据 (data
) 的内容类型,请改用 contentType: "json"
。 (更多内容在 the documentation。)
var postdata = JSON.stringify( { "username": document.getElementById("username").value, "password": document.getElementById("password").值 });
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: postdata,
dataType: "json",
contentType: "json", // <=== Added
success: getSuccess,
error: getFail
});
如果您不是要 发送 JSON,而是想发送通常的 URI 编码表单数据,则不会使用 JSON.stringify
并且只会将对象直接交给 jQuery 的 ajax
; jQuery 然后将创建 URI 编码的表单。
try {
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: {
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
},
dataType: "json",
success: getSuccess,
error: getFail
});
// ...
补充一下 T.J. 的回答,将 JSON 发送到 /token
端点不起作用的另一个原因是它不支持 JSON.
即使您将 $.ajax's
contentType 选项设置为 application/json
,就像您将 JSON 数据发送到 MVC 或 Web API 一样,/token
也赢了'接受那个有效载荷。它只支持形式 URL 编码对(例如 username=dave&password=hunter2)。如果你将一个对象传递给它的 data
选项,$.ajax
会自动为你编码,就像你的 postdata
变量,如果它没有被 JSON 字符串化的话。
此外,您必须记住在您的请求中包含 grant_type=password
参数(就像您的 PostAsync()
代码一样)。否则,/token
端点将响应 "invalid grant type" 错误,即使用户名和密码实际上是正确的。
发送表单数据时应使用jquery的$.param对数据进行urlencode。 AngularJs' $http 方法目前不执行此操作。
喜欢
var loginData = {
grant_type: 'password',
username: $scope.loginForm.email,
password: $scope.loginForm.password
};
$auth.submitLogin($.param(loginData))
.then(function (resp) {
alert("Login Success"); // handle success response
})
.catch(function (resp) {
alert("Login Failed"); // handle error response
});
从 angularjs 1.4 开始,这对于 $httpParamSerializerJQLike:
来说非常简单.controller('myCtrl', function($http, $httpParamSerializerJQLike) {
$http({
method: 'POST',
url: baseUrl,
data: $httpParamSerializerJQLike({
"user":{
"email":"wahxxx@gmail.com",
"password":"123456"
}
}),
headers:
'Content-Type': 'application/x-www-form-urlencoded'
})
})