Search code examples
c#asp.net-coreasp.net-core-mvcdotnet-httpclientasp.net-core-3.1

"No such host is known" when publishing locally, but not when debugging in Visual Studio


Related: No such host is known —> System.Net.Http.HttpRequestException:

So, I have an odd problem: whenever I perform any call to a RESTful API using HttpClient, I get a the following error: System.Net.Http.HttpRequestException: No such host is known

I've seen several other Q&As that discuss this, but many of these (such as the one I link to) discuss it specifically in the context of an Azure VM. Many of them suggest that this is caused by a DNS issue.

My situation is a little different the other Q&As I've read, though: when I run my web site by debugging in Visual Studio 2019, it runs just fine. However, when I publish to IIS locally, I get this exception whenever I try to do a call to HttpClient.

Also, unlike some of the other articles I've read about this, this does not happen intermittently for me - it happens every single time I try to do an API call.

If this is caused by a DNS issue like some of the other Q&As suggest, that suggests that there must be some difference between when I publish locally and when I debug in Visual Studio.

Here's how I configure my Publish (summary of the image below for those who prefer text): enter image description here

Image summary:

  • Web Deploy with WebPublishMethod MSDeploy
  • TargetFramework netcoreapp3.1
  • LastUsedBuildConfiguration Release
  • LastUsedPlatform x86
  • SelfContained false
  • MSDeployPublishMethod InProc
  • MSDeployServiceURL localhost

Additional details: - Destination URL http://localhost:80 - IIS Application Pool is set to "No managed code", as is usually suggested

Also, this occurs regardless of how I create the HttpClient instance and the code works just fine when debugging, so I think that this is more likely to be a configuration issue than a code issue, but for what it's worth, I use the following code in my Startup class to create it. (See also here for reference).

       services.AddHttpClient("MyNamedClient", (sp, c) =>
        {
            c.BaseAddress = new Uri("https://api.smartrecruiters.com/");

            c.Timeout = new TimeSpan(4, 0, 0);

            c.DefaultRequestHeaders.Accept.Clear();

            c.DefaultRequestHeaders.Add("Accept", "application/json");

            var ctx = sp.GetService<DatabaseContext>();

            Security security = ctx.Security.First();

            c.DefaultRequestHeaders.Add("APIToken", security.Apikey);
        });

I then use ASP.NET Core's Dependency Injection to get a HttpClient instance whenever I need it.

I also tried creating HttpClient as a singleton (which I know is not the recommended way of doing this in an ASP.NET Core application), but I had the same problem with that approach.

Here's the full error details from Event Viewer from when I try to do a GetAsync call using HttpClient:

Category: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware
EventId: 1
RequestId: 8000000c-0006-ff00-b63f-84710c7967bb
RequestPath: /UsersObject/GetUsersAPICall
SpanId: |f32bc4b9-41d453941179fa9f.
TraceId: f32bc4b9-41d453941179fa9f
ParentId: 

An unhandled exception has occurred while executing the request.

Exception: 
System.Net.Http.HttpRequestException: No such host is known.
 ---> System.Net.Sockets.SocketException (11001): No such host is known.
   at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at [Namespace].MyHttpClient.GetAsyncWithRetries(HttpClient client, String url) in C:\Users\[Removed]\source\repos\[Removed]\MyhHttpClient.cs:line 54
   at SR_Interaction.Models.SRUser.Search(String emailAddress, HttpClient client) in C:\Users\[Removed]\Models\SRUser.cs:line 100
   at Bosch_Live_Docs.Controllers.UsersObjectController.GetUsersAPICall(String query) in C:\Users\[Removed]\Controllers\UsersObjectController.cs:line 58
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

Anyone have any suggestions about how I might be able to fix this? I think it's probably either my IIS configuration or my publish settings - or am I on the wrong track entirely here?

Edit: As requested, here's the controller code:

    [HttpGet]
    [Authorize(Roles = "CreateUser,PatchUser")]
    public async Task<IActionResult> GetUsersAPICall([Bind("query")] [RegularExpression(@"^\w+(\.\w+)*@\w+(\.\w+)+|(\w+(\s\w+)*)$", ErrorMessage = "This does not look like a valid query")] string query)
    {
        if (query == null)
        {
            return BadRequest();
        }
        else if (!ModelState.IsValid)
        {
            // TODO: What to do with this? How do we actually show the validation error?
            return BadRequest("Does not look like a valid query");
        }

        List<SRUser> users = await SRUser.Search(query, clientFactory.CreateClient(Startup.srNamedClient));

        users = users.OrderBy(ur => ur.firstName).ToList();

        if (users.Any())
        {
            return PartialView("_UserList", users);
        }
        else
        {
            return NotFound();
        }
    }

And here's where I do the actual call:

public static async Task<List<SRUser>> Search(string emailAddress, HttpClient client)
    {
        string json;

        using (HttpResponseMessage msg = await client.GetAsync("user-api/v201804/users?limit=100&q=" + emailAddress))
        {
            json = await msg.Content.ReadAsStringAsync();
        }

        return JsonSerializer.Deserialize<NextPageContainer<SRUser>>(json).content;
    }

Further edit:

Here's the Application Pool:

enter image description here

Here are the modules I have installed:

enter image description here

I'm slightly confused about this particular one:

enter image description here

Is it necessary to include both? When I look at the Ordered view, the "old" one is actually higher: enter image description here

However, I can't reorder it.

Are these problems?

Even further edit: For the version that was published locally, I found that this problem occurs when my App Pool runs under a built-in account (such as ApplicationPoolIdentity). However, it's fixed when I use a specific user account. However, this does not work on the server.


Solution

  • It turns out that the requests being blocked by the corporate firewall. The server sits behind two firewalls, which is why changing the server's inbound or outbound rules didn't help me (because the other firewall was just blocking the call instead).

    To resolve this, I needed to use a proxy server. I looked up the fully qualified name of the server in the appropriate documentation. In my Startup.cs, I added the following logic:

    services.AddHttpClient("MyName", c =>
            {
                c.BaseAddress = new Uri("[Vendor API url]");
    
                c.Timeout = new TimeSpan(4, 0, 0);
    
                c.DefaultRequestHeaders.Accept.Clear();
    
                c.DefaultRequestHeaders.Add("Accept", "application/json");
    
                c.DefaultRequestHeaders.Add("ApiToken", RetrieveApiKey());
            })
                .ConfigurePrimaryHttpMessageHandler(() =>
                {
                    return new HttpClientHandler()
                    {
                        AllowAutoRedirect = true,
                        SslProtocols = SslProtocols.Tls13,
                        UseProxy = true,
                        Proxy = new WebProxy("ProxyServerName", 8080)
                        {
                            // The proxy server allows NTLM and Kerberos authentication
                            UseDefaultCredentials = true
                        }
                    };
                });