从外部网站获取令牌 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,我建议您检查一下。