几天来,我一直在为这件事绞尽脑汁,我需要向社会提出这个问题.我用C#开发已经有一段时间了,但对实际使用异步非常陌生.我正在制作一个rest API客户端,并决定使用RestSharp.我希望使用Execute或ExecuteTaskAsync,但似乎自从我上次查看RestSharp以来,这两个方法已被弃用,因此看起来我需要使RestClient上的ExecuteAsync工作.

在过go 的几天里,我一直在搜索StackOverflow和其他参考文献,试图梳理这一点.大多数参考资料似乎都很陈旧,它们都提到了几年前的Execute或ExecuteTaskAsync,所以我从所有这些来源中拼凑了出来,并花费了大量时间进行测试和重写.

我能够让下面的实现针对真实的API工作(出于业务目的调整了一些变量),但是类的实现工作得很好.它返回预期的RestResponse,我可以从中获取RestResponse.Content.

但是,当我try 为该方法设置单元测试时,在使用模拟版本的IRestClient时遇到了方法失败的问题.它一直说我有一个NullReferenceExcption,但不清楚问题的确切位置.对于这个样例,我使用的是.netFramework4.8和RestSharpv110.2.0.0.

作为异步的新手,我已经try 了各种不同的方法来配置模拟设置和返回,但我愿意判断和测试您可能对此有建议的任何其他配置.任何帮助我弄清楚我在哪里/为什么得到空引用的帮助都是非常感谢的.谢谢!

Class

using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyLib.Classes.Samples
{
    public interface IAuthSample
    {
        string GetResponse();
        Task<RestResponse> MakeAuthApiCallAsync();
    }

    public class AuthSample : IAuthSample
    {
        public string Url;
        public string Key;
        public string Secret;
        public IRestClient Client;

        public string GetResponse()
        {
            this.Url = "http://test.com";
            this.Key = "12345";
            this.Secret = "54321";

            var response = MakeAuthApiCallAsync().GetAwaiter().GetResult();
            return response.Content;
        }


        public virtual async Task<RestResponse> MakeAuthApiCallAsync()
        {
            var options = new RestClientOptions(this.Url)
            {
                MaxTimeout = -1,
            };

            // Set up api request
            var request = new RestRequest("oauth/token", Method.Post);
            request.AddHeader("Content-Type", "application/x-www-form-urlencoded");

            request.AddParameter("grant_type", "client_credentials");
            request.AddParameter("client_id", this.Key);
            request.AddParameter("client_secret", this.Secret);

            // Set up rest client if it doesn't exist
            IRestClient client = this.Client ?? new RestClient(options);

            // Contact api and get result
            var task = await client.ExecuteAsync<RestResponse>(request, CancellationToken.None);

            return task;
        }
    }
}

Unit Test

using MyLib.Classes;
using Moq;
using RestSharp;
using System.Net;
using MyLib.Classes.Samples;

namespace MyLib.Test.Classes.Samples
{
    [TestFixture()]
    public class AuthSampleTests
    {
        [Test()]
        public void GetResponseTest()
        {
            Assert.Fail();
        }

        [Test()]
        public void TestMakeAuthApiCall_SupplyParameters_MatchResponseResults()
        {

            // Arrange

            var url = "http://test.com";
            var key = "12345";
            var secret = "54321";
            var auth = new AuthSample()
            {
                Url = url,
                Key = key,
                Secret = secret
            };
            

            var response = new RestResponse()
            {
                Content = "blah",
                StatusCode = HttpStatusCode.OK
            };

            var restClient = new Mock<IRestClient>() { CallBase = true };

            restClient.Setup(x => x.ExecuteAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync(response);

            auth.Client = restClient.Object;

            // Act

            var result = auth.MakeAuthApiCallAsync().GetAwaiter().GetResult();

            // Assert

            Assert.Fail();

        }
    }
}

Exception and Stack

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=RestSharp
  StackTrace:
   at RestSharp.RestClientExtensions.<ExecuteAsync>d__10`1.MoveNext()
   at MyLib.Classes.Samples.AuthSample.<MakeAuthApiCallAsync>d__5.MoveNext() in C:\Users\bb\Documents\My Samples\VS Samples\MyApi\MyLib\Classes\Samples\AuthSample.cs:line 54

  This exception was originally thrown at this call stack:
    MyLib.Classes.Samples.AuthSample.MakeAuthApiCallAsync() in AuthSample.cs

推荐答案

将您的代码更改为:

// Contact api and get result
var task = await client.ExecuteAsync(request, CancellationToken.None).ConfigureAwait(false);

代码的"问题"在于,您实际调用的是扩展方法RestClientExtensions.ExecuteAsync<T>,如下所示:

if (request == null) throw new ArgumentNullException(nameof(request));

var response = await client.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return client.Serializers.Deserialize<T>(request, response, client.Options);

client.Serializers没有设置,是null.

您当前代码的另一个问题是,您的实际方法返回类型是RestResponse<RestResponse>(RestResponse<T>继承自RestResponse已经足够方便了,所以一切都可以编译并运行,您只需忽略T,即执行额外的丢弃工作).

Csharp相关问答推荐

我们应该如何在IHostedService中使用按请求的GbContent实例?

PredicateBuilder不是循环工作,而是手动工作

ASP.NET MVC购物车数量更新并从购物车中删除项目

在Linq中调用需要limit和offset的方法''''

我无法在Ubuntu下编译使用microsoft.extension.configurationbuilder jsonapi和mono mcs的c#应用程序

Blazor-从数据库内部服务器提取错误

Azure Redis缓存与Entra ID身份验证

WeakReference未被垃圾收集

如何在页面重新加载后保持菜单切换状态

MS Graph v5.42.0:在寻呼消息时更改页面大小

如何使用MoQ模拟Resources GroupCollection?

NET8 Maui&;iOS:AppCenter崩溃错误

正在try 从Blazor中的API读取JSON

使用System.Text.Json进行序列化时发生StackOverflow异常

如何在不复制或使用输出的情况下定义项目依赖

仅在ASP.NETCore应用程序中的附加单独端口上公开一组终结点

NETSDK1201:对于面向.NET 8.0和更高版本的项目,默认情况下,指定RUNTIME标识符将不再生成自包含的应用程序

项目参考和方法签名问题

读取测试项目中的应用程序设置

最小API定义的Swagger标头参数