2016-06-08 92 views
7

我已經在visual studio中創建了一個新的WebAPI解決方案,並且正在玩弄代碼來嘗試理解正在發生的事情。WebAPI2.0使用JSON的OWIN令牌請求

我有一個測試API,它們都與一個授權控制器和另一個實現所有實際功能的控制器一起運行。

的控制器(API)通過接收JSON和JSON的回覆,與/令牌request.This之外的所有工作必須是:

Content-Type: application/x-www-form-urlencoded 

否則,我只是得到一個錯誤回來。

的代碼創建該端點的部分似乎是這樣的:

OAuthOptions = new OAuthAuthorizationServerOptions 
{ 
    TokenEndpointPath = new PathString("/Token"), 
    Provider = new ApplicationOAuthProvider(PublicClientId), 
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), 
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), 
    // In production mode set AllowInsecureHttp = false 
    AllowInsecureHttp = false 
}; 

調用它像這樣導致200成功響應,具有承載的令牌:

$("#token_button").click(function() 
{ 
    var username = $("#token_email").val(); 
    var password = $("#token_password").val(); 

    postData("Token", "grant_type=password&username=" + username + "&password=" + password, "application/x-www-form-urlencoded", function (data) 
    { 
     user = data; 
     $("#feedback_display").html(user.access_token); 
    }, function() 
    { 
     user = null; 
    }); 
}); 

調用它像這導致400響應:

$("#token_button").click(function() 
{ 
    var username = $("#token_email").val(); 
    var password = $("#token_password").val(); 

    var data = { 
     "grant_type": "password", 
     "username": username, 
     "password": password 
    } 

    postData("Token", JSON.stringify(data), "application/json", function (data) 
    { 
     user = data; 
     $("#feedback_display").html(user.access_token); 
    }, function() 
    { 
     user = null; 
    }); 
}); 

響應正文是:

{"error":"unsupported_grant_type"} 

這裏唯一的區別是用於傳輸請求的編碼。 每一個我看所有的例子都使用表單編碼來請求這個令牌。

在/ api/Account/ExternalLogin下的代碼上放置一個斷點,永遠不會被擊中。

這是唯一一個接受表單編碼的原因嗎?如果沒有,我怎麼能改變控制器接受JSON?

或者我只是做了一些愚蠢的事情?

+1

不需要'JSON.stringify(data)' –

+0

感謝@gauravbhavsar現在的作品。如果您將其作爲答案發布,我會標記爲正確。我想這引發了問題,爲什麼我需要JSON.stringify發佈到其他端點的數據,但不是那個?我只是測試它,我確實需要。我會仔細研究一下,看看我能不能解決問題。 – Morvael

回答

11

使用application/x-www-form-urlencoded作爲Content-Type的原因很簡單:OAuth2 specification (RFC 6749)需要此內容類型用於令牌請求。

任何其他內容類型都會打破OAuth2兼容客戶端的兼容性。我建議你不要改變這種標準行爲。

注意
請注意這一點:

postData("Token", data, "application/json", function (data) 
{ 
    //... 
} 

作品只是因爲你是發送JSON了!即使您將application/json添加爲Content-Type標頭,您的請求正文也會被序列化爲表單鍵值對(AJAX調用中的jQuery默認對象序列化)。

OAuthAuthorizationServerMiddleware默認實現(更精確地在內部使用OAuthAuthorizationServerHandler)從Microsoft.Owin.Security.OAuth只是忽略Content-Type頭並試圖無論如何讀取請求體作爲一種形式。

+0

感謝您的全面解答。我沒有使用JSON.stringify()驗證POST數據不是JSON。我會遵循你的建議並放棄它,它只是略微提到這個呼叫必須是形式,其餘的是JSON。 – Morvael

+1

感謝您的鏈接。這對我幫助很大。有時閱讀協議定義更有意義:) –

1

無需JSON.stringify(data)傳遞數據直接。

+0

再次感謝您的幫助,但是我現在明白了爲什麼這會起作用,並且不覺得我可以將其標記爲正確的答案。 – Morvael

0

OAuth2需要application/x-www-form-urlencoded令牌請求的內容類型。

不過,我想過這個解決方法:

// GET api/Account/GetToken 
    [HttpPost] 
    [AllowAnonymous] 
    [Route("GetToken")] 
    public async Task<IHttpActionResult> GetToken(TokenRequest request) 
    { 
     var client = new HttpClient() 
     { 
      BaseAddress = new Uri(Request.RequestUri.GetLeftPart(UriPartial.Authority)) 
     }; 

     var content = new FormUrlEncodedContent(new[] 
     { 
      new KeyValuePair<string, string>("grant_type", "password"), 
      new KeyValuePair<string, string>("username", request.Username), 
      new KeyValuePair<string, string>("password", request.Password) 
     }); 

     var result = await client.PostAsync("/token", content); 
     string resultContent = await result.Content.ReadAsStringAsync(); 
     resultContent = resultContent.Replace(".issued", "issued").Replace(".expires", "expires"); 
     TokenResponse tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(resultContent); 

     return Ok(tokenResponse); 
    } 

型號:

public class TokenRequest 
    { 
     public string Username { get; set; } 
     public string Password { get; set; } 
    } 

    public class TokenResponse 
    { 
     public string access_token { get; set; } 
     public string token_type { get; set; } 
     public int expires_in { get; set; } 
     public string userName { get; set; } 
     public DateTime issued { get; set; } 
     public DateTime expires { get; set; } 
     public string error { get; set; } 
     public string error_description { get; set; } 
    } 

可以改善,但偉大工程。