HttpClient PostAsync等价于JQuery的FormURLEncodedContent而不是JSON

本文关键字:JSON FormURLEncodedContent PostAsync 等价于 JQuery HttpClient | 更新日期: 2023-09-27 18:04:40

我编写了一个JQuery脚本来执行用户登录POST(尝试在附加信息部分使用c#完成,见下文)。
在用JQuery代码从我的html页面触发POST后,我发现了以下问题:
1 -我调试到服务器端代码,我知道POST被服务器接收(在ValidateClientAuthentication()函数中,但不在GrantResourceOwnerCredentials()函数中)。
2 -另外,在服务器端,我找不到用户名密码的任何迹象,应该与postdata一起发布。然而,对于用户端c#代码,当我调试到服务器端c#代码时,我可以在上下文变量中看到这些值。我认为,这就是所有问题的根源。
JQuery代码调用函数getFail()

?-我想知道,这是什么JQuery代码做不同于 c#用户端代码下面,以及我如何修复它,所以他们做同样的工作?

(我的猜测:, JSON。stringifyFormURLEncodedContent做一些不同的事情)

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
};

};

服务器端处理后(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);
}
//...

HttpClient PostAsync等价于JQuery的FormURLEncodedContent而不是JSON

不幸的是,dataType控制jQuery期望的返回的数据是什么,而不是data。设置请求数据的内容类型(data),用contentType: "json"代替。(详见文档)

var postdata = JSON.stringify({"用户名":. getelementbyid("用户名")value,"密码":. getelementbyid("密码")value});

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
    });
    // ...
<p另一个原因是,发送JSON到/token端点不起作用,只是因为它不支持JSON。>

即使你设置$.ajax's contentType选项为application/json,就像你要发送JSON数据到MVC或Web API, /token不会接受有效载荷。它只支持表单URLencoded对(例如username=dave&password=hunter2)。如果你将一个对象传递给它的data选项,$.ajax会自动为你编码,就像你的postdata变量一样,如果它没有被JSON字符串化。

另外,您必须记住在请求中包含grant_type=password参数(正如您的PostAsync()代码所做的那样)。否则,即使用户名和密码实际上是正确的,/token端点也会响应一个"invalid grant type"错误。

你应该使用jquery的$。参数用于在发送表单数据时对数据进行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'
    })
})