Исключение NullReferenceException при тестировании API, реализованного с помощью Refit

#c# #rest #unit-testing #nullreferenceexception #refit

Вопрос:

Я пытаюсь проверить некоторые ответы на ошибки (неверный запрос, несанкционированный, …) с помощью Refit, и поэтому я реализовал тест-манипулятор, который возвращает любой желаемый ответ. Ответ отлично работает с ответом «ОК» (код состояния HTTP 200) .:

 public class Program
{
    private static async Task Main()
    {
        var api = RestService.For<ITest>(
            new HttpClient(new TestHandler(HttpStatusCode.OK))
                {
                    BaseAddress = new Uri("https://example.com")
                }
            );
        Console.WriteLine(await api.Test("foo").ConfigureAwait(false));
    }
}

public interface ITest
{
    [Get("/foo/{bar}")]
    Task<string> Test(string bar);
}

public class TestHandler : HttpMessageHandler
{
    private readonly HttpResponseMessage _response;

    public TestHandler(HttpStatusCode httpStatusCode)
        => _response = new HttpResponseMessage(httpStatusCode) { Content = new StringContent("Yay!") };

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        => Task.FromResult(_response);
}
 

Однако всякий раз, когда я изменяю код состояния ответа, например, на BadRequest (400), NotFound (404) или Unauthorized (401), при обновлении возникает NullReferenceException :

 Object reference not set to an instance of an object.
   at Refit.DefaultApiExceptionFactory.<CreateExceptionAsync>d__4.MoveNext() in /_/Refit/RefitSettings.cs:line 183
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Refit.RequestBuilderImplementation.<>c__DisplayClass14_0`2.<<BuildCancellableTaskFuncForMethod>b__0>d.MoveNext() in /_/Refit/RequestBuilderImplementation.cs:line 313
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at RefitTest.Program.<Main>d__0.MoveNext() in C:UsersRobSourceReposRefitTestRefitTestProgram.cs:line 18
 

Это указывает на строку 183 RefitSettings.cs. Но я не могу понять, почему 200 ОК сработает, но любой другой ответ не сработает? Что я делаю не так?

Редактировать: В попытке отладить это дальше, я клонировал Refit и заменил пакет Refit NuGet ссылкой на проект Refit. Это приводит к InvalidOperationException: ITest doesn't look like a Refit interface. Make sure it has at least one method with a Refit HTTP method attribute and Refit is installed in the project . Кроме того, возврат к нескольким версиям (я перешел на 5.2.4) не помогает.

Комментарии:

1. This points to RefitSettings.cs line 183. But I can't figure out why a 200 OK would work but any other response won't? github.com/reactiveui/refit/blob/… этот метод вызывается для неудачных ответов, таких как 400, 404, 401… И, вероятно, вы получаете NullReferenceException это потому, что ! (не прощающий) docs.microsoft.com/en-us/dotnet/csharp/language-reference/…` поддерживается только в C# 8

2. @демо У меня есть <LangVersion>latest</LangVersion> в моем файле проекта .net 5.0.

3. @demo Также добавил <Nullable>enable</Nullable> , на всякий случай, никакой разницы.

4. тогда похоже, что ответ равен нулю… или я не понимаю, что/почему…

5. @demo Ответ содержится в коде: new StringContent("Yay!") (и да, я также пробовал допустимый JSON, на случай, если при переоборудовании ожидался бы JSON). И, как указано в вопросе: он отлично работает с 200 ок. Просто не со статусами 4xx (еще не пробовал 3xx или 5xx).

Ответ №1:

Нашел его с помощью коллеги! Оказывается HttpResponseMessage , нужны некоторые/любые RequestMessage .

Изменить

 public TestHandler(HttpStatusCode httpStatusCode)
    => _response = new HttpResponseMessage(httpStatusCode)
    { 
        Content = new StringContent("Yay!") 
    };
 

Для:

 public TestHandler(HttpStatusCode httpStatusCode)
    => _response = new HttpResponseMessage(httpStatusCode)
    {
        RequestMessage = new(),  // <-- This one here...
        Content = new StringContent("Yay!")
    };
 

И это работает так, как и ожидалось. Как и в вопросе (и комментариях), я был близок, но, по-видимому, в полусне, потому что это именно то, на что он указал мне.

Я представил здесь проблему.