JsonServiceClient throws notfound, HttpClient says OK

I have an integration test which spins up an apphost. I can send a request to it using the .net core HttpClient returns ok, but using JsonServiceClient throws a not found exception. Both clients are initialized to the same base url (http://localhost/) and are requesting the same uri (/metadata).

I printout the requests from both clients and have tried but I do not understand why its not working:

JsonServiceClient Initialization

        public JsonServiceClient CreateJsonServiceClient(ITestOutputHelper testOutputHelper)
        {
            var client = new JsonServiceClient(this.ClientOptions.BaseAddress.ToString());

            client.RequestFilter = req =>
                testOutputHelper.WriteLine($"REQ: {req.Method} {req.RequestUri}");
            client.ResponseFilter = res =>
                testOutputHelper.WriteLine($"RSP: {res.StatusCode} {res.ResponseUri}");

            return client;
        }

HttpClient initialization

            this.httpClient =
                appHostFixture.CreateDefaultClient(new LoggingHandler(new HttpClientHandler(), testOutputHelper));

The test

        [Fact]
        public async Task GetWithDefaultValuesShouldReturnAuthorizedPrices()
        {
            var responseHttp = await this.httpClient.GetAsync(new Uri("/metadata", UriKind.Relative)).ConfigureAwait(false);
            var responseContent = await responseHttp.Content.ReadAsStringAsync().ConfigureAwait(false);

            var metadata = this.client.Get("/metadata");
            var metadataString = metadata.ReadToEnd();
            metadataString.PrintDump();
        }

Output

BokaMera.Api.IntegrationTest.Tests.ServiceStack.ServicePriceServiceTests.GetWithDefaultValuesShouldReturnAuthorizedPrices

ServiceStack.WebServiceException : Not Found
   at ServiceStack.ServiceClientBase.ThrowWebServiceException[TResponse](Exception ex, String requestUri) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\ServiceClientBase.cs:line 896
   at ServiceStack.ServiceClientBase.ThrowResponseTypeException[TResponse](Object request, Exception ex, String requestUri) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\ServiceClientBase.cs:line 831
   at ServiceStack.ServiceClientBase.HandleResponseException[TResponse](Exception ex, Object request, String requestUri, Func`1 createWebRequest, Func`2 getResponse, TResponse& response) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\ServiceClientBase.cs:line 782
   at ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\ServiceClientBase.cs:line 1360
   at ServiceStack.ServiceClientBase.Get(String relativeOrAbsoluteUrl) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\ServiceClientBase.cs:line 1417
   at BokaMera.Api.IntegrationTest.Tests.ServiceStack.ServicePriceServiceTests.GetWithDefaultValuesShouldReturnAuthorizedPrices() in D:\bokamera\src\bokamera-api-v2\Tests\BokaMera.Api.IntegrationTest\Tests\ServiceStack\ServicePriceServiceTests.cs:line 77
--- End of stack trace from previous location where exception was thrown ---



Request:
Method: GET, RequestUri: 'http://localhost/metadata', Version: 1.1, Content: <null>, Headers:
{
}

Response:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Request-Context: appId=
  Set-Cookie: ss-pid=GAgqH1zIvIMkgOUsORRQ; expires=Sat, 21 Jul 2040 10:19:37 GMT; path=/; samesite=none; httponly
  Set-Cookie: ss-id=KOavlRqM8ciDZPHpZ2uN; path=/; samesite=none; httponly
  Content-Type: text/html; charset=utf-8
}

REQ: GET http://localhost/metadata

The order in the output is a bit wrong (exception comes first) but you can see that the httpclient succeeds but the jsonserviceclient fails, for the same url. Why?

If you want to compare 2 different requests, view the raw HTTP Request Headers they both send so you can compare & contrast the actual differences. You’d typically want to look at the raw HTTP Request Headers for any integration issues or if you just want to analyze their behavior.

The Service Clients should only be used for Service requests, if you want to request the Metadata HTML pages you should use a generic HTTP Client instead like HttpClient or HTTP Utils, e.g:

var html = "http://localhost/metadata".GetStringFromUrl();

I have dumped the full request objects and matched the headers (the only ones being used are accept application/json and also content-type applications). No combination of these worked with the JsonServiceClient. All combinations worked with the HttpClient.

The JsonServiceClient is acting as if the url is unavailable.

I started with a service request like this:

            var query = new ServicePricesQuery();
            var response = await this.client
                .GetAsync<ServicePriceResponse>(query)
                .ConfigureAwait(false);

Thats when I first encountered the error and that was the reason I requested the metadatapage instead (in order to remove error sources).

Using

var html = "http://localhost/metadata".GetStringFromUrl();

Also throws a “not found” exception even if the line just before it was able to retrieve the html using the other client.

I tried initializing a raw HttpClient myself and then I got the same not found exception.

It appears that the method WebApplicationFactory.CreateClient adds some special handler (Microsoft.AspNetCore.TestHost.ClientHandler) to make things work in test scenarios. Is it possible to provide custom HttpMessageHandlers to JsonServiceClient somehow?

Something sounds off here, can you view http://localhost/metadata in a browser? If you can you should be able to view it from HTTP Utils. Are you able to get the raw HTTP Headers?

The *ServiceClient .NET Service Clients utilizes .NET’s HttpWebRequest.

The JsonHttpClient utilizes .NET’s HttpClient, you can download it from the ServiceStack.HttpClient NuGet package. Which lets you configure which HttpMessageHandler to use using either GlobalHttpMessageHandlerFactory or HttpMessageHandler instance property.

Changing to JsonHttpClient and using the following initialization code works:

            var client = new JsonHttpClient(this.ClientOptions.BaseAddress.ToString());
            client.HttpClient = this.CreateClient();

            client.RequestFilter = req =>
                testOutputHelper.WriteLine($"REQ: {req.Method} {req.RequestUri}");
            client.ResponseFilter = res =>
                testOutputHelper.WriteLine($"RSP: {res.StatusCode}");

            return client;

I am unsure of how to test in a browser, since the server gets started and disposed by a test. If I set a breakpoint, I guess I will get no response either way?

I guess that the way WebApplicationFactory.Create initializes a HttpClient is special somehow?

For me, using JsonHttpClient is good enough. Thank you for your help.

But it sure would be interesting to understand whats happening…

You would put a Thread.Sleep() after the server starts for 10-30s so you can test it from the browser, if you can’t connect to it from the browser then I’m assuming your not running a full Integration Test and some sort of fake in-process Test Server.