'Ocelot API Gateway error: No connection could be made because the target machine actively refused it

I am trying to build a microservice architecture system using Ocelot as my API Gateway. I have had the current problem for about 2 weeks and have not been able to figure out why it is happening, so I am turing to the experts and hopefully you can help :).

The structure at the moment is as follows: I have an AccountAPI and an Ocelot API Gateway. I have added the route from the API Gateway to the AccountAPI according to the docs. When I run the project, I can call the AccountAPI directly, and it works fine. But when I call through the API Gateway, I get this error.

Error Code: ConnectionToDownstreamServiceError Message: Error connecting to downstream service, exception: 
System.Net.Http.HttpRequestException: No connection could be made because the target machine actively refused it. 
(localhost:5101)

Quite obviously, it is not even reaching the controller of the AccountAPI, so I can't even debug >.>

The fact that I can access the object without going through the gateway tells me it is not a network / firewall issue (I turned off all my firewalls just to test, and got the same result).

This is what is in my ocelot.json file (which should connect Ocelot to the AccountAPI)

{
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5111/"
  },
  "Routes": [
    {
      "DownstreamPathTemplate": "/Account/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5101
        }
      ],
      "UpstreamPathTemplate": "/MeHealth/Account/{url}",
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
      "Priority": 1
    }
  ]
}

So if I call https:// localhost:5101/Account/1 I get my correct object returned. But with calling https:// localhost:5111/MeHalth/Account/1 I get the error. (I did also try http:// (for those who caught the scheme) and I get the same result as https)).

Other things to note: Both of these are deployed through the docker-compose with the following config:

services:
    ocelotapigateway:
        environment:
            - ASPNETCORE_ENVIRONMENT=Development
            - ASPNETCORE_URLS=https://+:443;http://+:80
        ports:
            - "5111:443"
        volumes:
            - ${APPDATA}/Microsoft/UserSecrets:C:\Users\ContainerUser\AppData\Roaming\Microsoft\UserSecrets:ro
            - ${APPDATA}/ASP.NET/Https:C:\Users\ContainerUser\AppData\Roaming\ASP.NET\Https:ro

    accountapi:
        environment:
            - ASPNETCORE_ENVIRONMENT=Development
            - ASPNETCORE_URLS=https://+:443;http://+:80
        ports:
            - "5101:443"
        volumes:
            - ${APPDATA}/Microsoft/UserSecrets:C:\Users\ContainerUser\AppData\Roaming\Microsoft\UserSecrets:ro
            - ${APPDATA}/ASP.NET/Https:C:\Users\ContainerUser\AppData\Roaming\ASP.NET\Https:ro

Any advice or help that you can give will be greatly appreciated. Please also let me know if you need extra information on something.



Solution 1:[1]

The problem is localhost on your host machine is not same as localhost when running in docker container. Think about docker as isolated "virtual machine" with its own localhost. When your api gateway tries to reach localhost:5101 it fails because it not exists in this "virtual machine", it's running on your host machine. You have 2 solutions here:

  1. Use host.docker.internal and external port instead of localhost: https://host.docker.internal:5101.
  2. When running 2 containers under same non-default docker network (that's what happens when you run docker-compose) they can reach each other by service name and internal port: https://accountapi:443

Solution 2:[2]

I also trapped in the same problem just only need to commented out in API's startup file

app.UseHttpsRedirection();

and in ocelot file

 "Host": "host.docker.internal",
 "Port": 5101

OR

 "Host": "service-name",
 "Port": 80

Solution 3:[3]

Do not use https (and remove app.UseHttpsRedirection())

Use docker internal host name: "Host": "host.docker.internal"

Below is working example;

Ocelot.json

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/v1/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "host.docker.internal",
          "Port": 7001
        }
      ],
      "UpstreamPathTemplate": "/article-service/{everything}",
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]
    },
    {
      "DownstreamPathTemplate": "/api/v1/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "host.docker.internal",
          "Port": 7002
        }
      ],
      "UpstreamPathTemplate": "/review-service/{everything}",
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]
    }
  ],

  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:7000"
  }
}

docker-compose.yml

version: '3.4'

services:
  data: 
    container_name: sqlserver
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment: 
      - SA_PASSWORD=@P****
      - ACCEPT_EULA=Y
    ports:
        - "1433:1433"
    volumes:
        - ./APPDATA:/var/opt/mssql/data 

  myapp.apigateway:
    image: ${DOCKER_REGISTRY-}myappapigateway
    build:
      context: .
      dockerfile: MyApp.ApiGateway/Dockerfile
    ports:
    - "7000:80"

  myapp.articleservice:
    image: ${DOCKER_REGISTRY-}myapparticleservice
    build:
      context: .
      dockerfile: MyApp.ArticleService/Dockerfile
    ports:
    - "7001:80"

  myapp.reviewservice:
    image: ${DOCKER_REGISTRY-}myappreviewservice
    build:
      context: .
      dockerfile: MyApp.ReviewService/Dockerfile
    ports:
    - "7002:80"

docker-compose.override.yml

version: '3.4'

services:
  myapp.apigateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

  myapp.reviewservice:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

  myapp.articleservice:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Saad Awan
Solution 3