从外部网站获取令牌 API 正在返回 system.threading.tasks
Getting a token from an external web API is returning system.threading.tasks
我成功地从我的 GetAccessToken()
和 GetAccessTokenAsync
方法中取回了一个令牌,但是直到 GetCourses
的主要方法之后才检索到令牌,这不会'不起作用,因为这是收集我需要在我的 cshtml 页面上显示的数据的方法。我试过拆开这个控制器并创建一个 Globals class,它将只包含 URI、apiKey 和令牌,但后来我读到这对 MVC
来说是不好的做法,所以我放弃了这种努力。反正它是在 GetCourses
方法之后被调用的,所以它也是死路一条。
我是 MVC 的新手,来自 WebForms
背景,我习惯于在我的 PageInit
中使用这种代码,但我正在努力弄清楚如何在 MVC
中完成此操作。有人可以帮我弄清楚我做错了什么,或者我是否需要以不同的方式解决这个问题?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
这不是异步在 C# 中的工作方式。您需要制作 GetCourses()
async
并等待 GetAccessToken()
,或者使用肮脏的 hack GetAccessToken().GetAwaiter().GetResult()
,但在某些情况下它可能变得不安全。
C# 中等待任务完成的(非阻塞)方式是使用 await
关键字。对于使用await
关键字的方法,它必须被标记为async
。通过使用 await
,您不仅可以等待任务完成,而且当前线程也不会被阻塞。在另一个方法中包装异步操作不会使其同步。换句话说,异步性质向上传播调用层次结构,调用者必须等待。所以,GetAccessToken()
还是要等。控制器操作也可以标记为异步,因此您可能需要:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
在调用 ToString()
之前请注意上面的附加括号。但是,由于 GetAccessToken()
已经 returns 一个 string
,您不需要多余的 ToString()
调用:
TempData["Token"] = await GetAccessToken();
现在,您还可以更改此设置:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
只是:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft 在异步编程方面做得很好documentation,我建议您检查一下。
我成功地从我的 GetAccessToken()
和 GetAccessTokenAsync
方法中取回了一个令牌,但是直到 GetCourses
的主要方法之后才检索到令牌,这不会'不起作用,因为这是收集我需要在我的 cshtml 页面上显示的数据的方法。我试过拆开这个控制器并创建一个 Globals class,它将只包含 URI、apiKey 和令牌,但后来我读到这对 MVC
来说是不好的做法,所以我放弃了这种努力。反正它是在 GetCourses
方法之后被调用的,所以它也是死路一条。
我是 MVC 的新手,来自 WebForms
背景,我习惯于在我的 PageInit
中使用这种代码,但我正在努力弄清楚如何在 MVC
中完成此操作。有人可以帮我弄清楚我做错了什么,或者我是否需要以不同的方式解决这个问题?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
这不是异步在 C# 中的工作方式。您需要制作 GetCourses()
async
并等待 GetAccessToken()
,或者使用肮脏的 hack GetAccessToken().GetAwaiter().GetResult()
,但在某些情况下它可能变得不安全。
C# 中等待任务完成的(非阻塞)方式是使用 await
关键字。对于使用await
关键字的方法,它必须被标记为async
。通过使用 await
,您不仅可以等待任务完成,而且当前线程也不会被阻塞。在另一个方法中包装异步操作不会使其同步。换句话说,异步性质向上传播调用层次结构,调用者必须等待。所以,GetAccessToken()
还是要等。控制器操作也可以标记为异步,因此您可能需要:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
在调用 ToString()
之前请注意上面的附加括号。但是,由于 GetAccessToken()
已经 returns 一个 string
,您不需要多余的 ToString()
调用:
TempData["Token"] = await GetAccessToken();
现在,您还可以更改此设置:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
只是:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft 在异步编程方面做得很好documentation,我建议您检查一下。