I am using HttpClient.GetAsync
to request a microservice that may or may not run on a target server.
My code is like this:
// htmlAddress is actually a method parameter.
// It contains a uri that the user enters.
// At this uri, an Apache server handles HTTP or HTTPS requests.
// I may assume this Apache server is available.
// An example uri would be:
var htmlAddress = new Uri("http://my.server.domain/path");
// Alongside there may or may not be a microservice running.
// If it runs, it requires https and listens on port 19500,
// e.g. https://my.server.domain:19500/path/route/to/endpoint
var builder = new UriBuilder(htmlAddress)
{
Scheme = "https",
Port = 19500
};
using var restClient = new HttpClient
{
BaseAddress = builder.Uri,
Timeout = TimeSpan.FromSeconds(5)
};
var response = await restClient.GetAsync("route/to/endpoint").ConfigureAwait(false);
// ...
The problem is that GetAsync
throws an HttpRequestException
that does not have a StatusCode
, i.e. that property is null
. I had hoped for a response with 404 NotFound
, like when navigating to any other URL that does not exist.
According to the documentation, an HttpRequestException
might also be thrown if there was something wrong with the SSL certificate or a DNS problem for example.
I want to implement a fallback for when the service isn't there. How can I reliably identify the condition "the service does not exist" from the code, and differentiate it from, say, a DNS lookup error? I don't want to match the exception's Message
"No connection could be made because the target machine actively refused it", as I think that text is going to be localized.
You are not getting a 404
, because if the service doesn't exist, there is virtually nothing listening on port 19500. That port might even be closed by the server's firewall. When you say "like when navigating to any other URL that does not exist", you are referring to a regular http(s) request without any port. Those requests will then use a default port for the protocol (80/443), and the Apache process is listening on those ports, handling the request, and actively returning the 404
error when the requested resource does not exist. If the Apache service wasn't running, you'd probably not get the 404
there, either.
Nevertheless you can identify this condition within your code quite easily: the HttpRequestException
will contain an inner SocketException
, which has a SocketErrorCode
property of enum type SocketError
. The enum member ConnectionRefused
is what you are looking for.
You can use a when
exception filter to detect this specific case:
try
{
var response = await restClient.GetAsync("route/to/endpoint").ConfigureAwait(false);
// ...
}
catch (HttpRequestException ex) when (
ex.InnerException is SocketException se &&
se.SocketErrorCode is SocketError.ConnectionRefused)
{
// The service was not available
}