获取 ASP.NET MVC 5 Web API 令牌有时会失败
Posted
技术标签:
【中文标题】获取 ASP.NET MVC 5 Web API 令牌有时会失败【英文标题】:Get ASP.NET MVC5 WebAPI token fails sometimes 【发布时间】:2016-04-29 11:35:25 【问题描述】:获取 ASP.NET MVC5 WebAPI 令牌有时会失败
代码
string GetAPITokenSync(string username, string password, string apiBaseUri)
var token = string.Empty;
using (var client = new HttpClient())
client.BaseAddress = new Uri(apiBaseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(60);
//setup login data
var formContent = new FormUrlEncodedContent(new[]
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password),
);
//send request
Task t = Task.Run(() =>
HttpResponseMessage responseMessage = client.PostAsync("/Token", formContent).Result;
var responseJson = responseMessage.Content.ReadAsStringAsync().Result;
var jObject = JObject.Parse(responseJson);
token = jObject.GetValue("access_token").ToString();
);
t.Wait();
t.Dispose();
t = null;
GC.Collect();
return token;
错误
发生了一个或多个错误。 ---> System.AggregateException:一个或 发生了更多错误。 ---> System.Threading.Tasks.TaskCanceledException:任务已取消。 --- 内部异常堆栈跟踪结束 --- 在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceled 异常)在 System.Threading.Tasks.Task
1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task
1.get_Result()
WebAPi 登录方式默认没有变化。
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public HttpResponseMessage Login(string username, string password)
try
var identityUser = UserManager.Find(username, password);
if (identityUser != null)
var identity = new ClaimsIdentity(Startup.OAuthOptions.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, username));
AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(1440));
var token = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
var response = new HttpResponseMessage(HttpStatusCode.OK)
Content = new ObjectContent<object>(new
UserName = username,
ExternalAccessToken = token
, Configuration.Formatters.JsonFormatter)
;
return response;
catch (Exception)
return new HttpResponseMessage(HttpStatusCode.BadRequest);
Startup 类默认没有变化
public partial class Startup
public static OAuthAuthorizationServerOptions OAuthOptions get; private set;
public static string PublicClientId get; private set;
public void ConfigureAuth(IAppBuilder app)
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
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 = true
;
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
有什么线索吗?
【问题讨论】:
不是超时了吗? 60秒后?你为什么要在一个任务上运行 Post?然后阻塞你的调用线程等待任务完成? @BrunoGarcia 我猜 60 秒就可以拿到令牌了。请提出一些需要考虑的建议...谢谢! 我的意思是:问题不是因为您的通话超时了吗?是因为它无法到达服务器还是需要太长时间(超过 60 秒)才能完成?特别是如果它有时只会失败 @BrunoGarcia 它应该可以工作......我也会尝试这个解决方案***.com/questions/30656795/… @Dimi 我认为您没有回答是否超时的问题。是吗? 【参考方案1】:很难确定,但阻止 HttpClient 调用的方式无济于事。 HttpClient 是一个仅异步库;您可能会遇到死锁情况。我建议摆脱所有.Result
s 和.Wait()
s 并使用async/await
异步编写所有内容。而且您的 Task.Run 没有取得任何成果,所以应该去。
我知道这是从控制台应用移植过来的 Topshelf 应用。我对 Topshelf 不是很熟悉,但我认为,就像控制台应用程序一样,您需要在 某处 进行阻止,否则您的应用程序将直接退出。执行此操作的位置位于最顶部 - 应用程序的入口点。
这演示了该模式,并重写了您的 GetApiToken
方法:
// app entry point - the only place you should block
void Main()
MainAsync().Wait();
// the "real" starting point of your app logic. do everything async from here on
async Task MainAsync()
...
var token = await GetApiTokenAsync(username, password, apiBaseUri);
...
async Task<string> GetApiTokenAsync(string username, string password, string apiBaseUri)
var token = string.Empty;
using (var client = new HttpClient())
client.BaseAddress = new Uri(apiBaseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(60);
//setup login data
var formContent = new FormUrlEncodedContent(new[]
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password),
);
//send request
HttpResponseMessage responseMessage = await client.PostAsync("/Token", formContent);
var responseJson = await responseMessage.Content.ReadAsStringAsync();
var jObject = JObject.Parse(responseJson);
token = jObject.GetValue("access_token").ToString();
return token;
【讨论】:
以上是关于获取 ASP.NET MVC 5 Web API 令牌有时会失败的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ASP.NET 5 MVC 6 保护 Web API
使用 ASP.NET 5 MVC 6 Web API 进行 Cookie 身份验证
如何在 ASP.NET Core MVC 中请求和显示实时 Web api 数据?
如何将 Web API 添加到现有的 ASP.NET MVC (5) Web 应用程序项目中?