簡體   English   中英

如何從另一個服務調用 .NET Core Web Api,兩者都在單獨的 docker 容器中運行

[英]How to call .NET Core Web Api from another service, both running in separate docker containers

在我的場景中,我有兩個在兩個單獨的 docker 容器上運行的 .NET Core Web API。

第一個服務稱為Catalog.API ,這是控制器的代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Catalog.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Catalog.API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class CatalogController : ControllerBase
    {
        private readonly CatalogItemContext _context;

        public CatalogController(CatalogItemContext context)
        {
            _context = context;
        }


        [Route("items")]
        [HttpGet]
        public async Task<ActionResult<List<CatalogItem>>> GetItemsAsync()
        {
            var items = await _context.CatalogItems.ToListAsync();
            if (items == null)
            {
                return NotFound();
            }
            return items;
        }

        [Route("items/{id}")]
        [HttpGet]
        public async Task<ActionResult<CatalogItem>> GetItemByIdAsync(int id)
        {
            var item = await _context.CatalogItems.SingleOrDefaultAsync(model => model.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return item;
        }

        [HttpPost]
        [Route("items")]
        public async Task<ActionResult> CreateProductAsync([FromBody] CatalogItem product)
        {
            var item = new CatalogItem(product.Name, product.Price, product.Count);
            _context.CatalogItems.Add(item);
            await _context.SaveChangesAsync();
            return CreatedAtAction(nameof(GetItemByIdAsync), new {id = item.Id}, null);
        }

        [HttpPut]
        [Route("items")]
        public async Task<ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate)
        {
            try
            {
                _context.Entry(productToUpdate).State = EntityState.Modified;
                await _context.SaveChangesAsync();
                return CreatedAtAction(nameof(GetItemByIdAsync), new {id = productToUpdate.Id}, null);
            }
            catch (Exception ex)
            {
                return BadRequest();
            }
        }
        
        [HttpPost]
        [Route("items/{id}")]
        public async Task<ActionResult> DeleteProductById(int id)
        {
            var itemToDelete = _context.CatalogItems.SingleOrDefault(model => model.Id == id);
            if (itemToDelete == null)
            {
                return NotFound();
            }

            _context.CatalogItems.Remove(itemToDelete);
            await _context.SaveChangesAsync();
            return NoContent();
        }
    }
}

如您所見,我有一些基本的方法,到目前為止沒有什么特別的。 該服務在localhost:80上運行(例如, http://localhost:80/api/catalog/items

這是 Catalog.API 的 dockerfile,通過 docker-compose.yml 調用:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src

COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj /src/csproj-files/

WORKDIR ./csproj-files
RUN dotnet restore


WORKDIR /src

COPY . .
WORKDIR /src/src/Services/Catalog/Catalog.API/
RUN dotnet publish -c Release -o /app

FROM build AS publish

FROM base as final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Catalog.API.dll"]

第二個服務稱為Basket.API ,這是控制器:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Basket.API.Models;
using Basket.API.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Basket.API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class BasketController : ControllerBase
    {
        private readonly BasketContext _context;
        private readonly IBasketService _basketService;

        public BasketController(BasketContext context, IBasketService basketService)
        {
            _context = context;
            _basketService = basketService;
        }

        [HttpGet]
        [Route("entries")]
        public async Task<ActionResult<List<Models.Basket>>> GetAllBasketAsync()
        {
            var basketList = await _context.UserBaskets.Include(basket => basket.Items).ToListAsync(); //include needed to load Items List
            if (basketList == null)
            {
                return NoContent();
            }

            return basketList;
        }

        [HttpGet]
        [Route("entries/{id}")]
        public async Task<ActionResult<Models.Basket>> GetBasketByIdAsync(int id)
        {
            var basket = await _context.UserBaskets.Where(b => b.UserId == id).Include(m => m.Items).SingleOrDefaultAsync();
            if (basket == null)
            {
                return NoContent();
            }

            return basket;
        }

        [HttpGet]
        [Route("test")]
        public async Task<ActionResult> TestCall()
        {
            var test1 = await _basketService.GetBasketByIdAsync(1);
            return Ok(test1);
        }
    }
}

在控制器內部,我使用了一個名為 BasketService 的類,它被注入到Startup.cs中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Basket.API.Models;
using Basket.API.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Basket.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<BasketContext>(builder =>
            {
                builder.UseSqlServer(Configuration.GetConnectionString("Default"));
            });

            services.AddHttpClient<IBasketService, BasketService>();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

這是BasketService.cs的代碼

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Basket.API.Services
{
    public class BasketService : IBasketService
    {
        private readonly HttpClient _client;

        public BasketService(HttpClient client)
        {
            _client = client;
        }
        
        public async Task<IEnumerable<Models.Basket>> GetAllBasketAsync()
        {
            var stringContent = await _client.GetStringAsync("http://localhost:80/api/basket/entries");
            return JsonConvert.DeserializeObject<List<Models.Basket>>(stringContent);
        }

        public async Task<Models.Basket> GetBasketByIdAsync(int id)
        {
            var stringContent = await _client.GetStringAsync("http://localhost:80/api/basket/entries/" + id);
            return JsonConvert.DeserializeObject<Models.Basket>(stringContent);
        }
    }
}

Basket.APIlocalhost:81上運行,這是 Dockerfile,它也被 docker-compose.yml 調用:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 81

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src

COPY src/Services/Basket/Basket.API/Basket.API.csproj /src/csproj-files/

WORKDIR ./csproj-files
RUN dotnet restore

WORKDIR /src

COPY . .
WORKDIR /src/src/Services/Basket/Basket.API/
RUN dotnet publish -c Release -o /app

FROM build AS publish

FROM base as final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Basket.API.dll"]

碼頭工人-compose.yml:

version: '3.4'

services:

  sqldata:
    ports:
      - 1433:1433
    image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=Pass@word
  
  catalog.api:
    ports:
      - 80:80
    build:
      context: .
      dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
    depends_on:
      - sqldata

  basket.api:
    ports:
      - 81:80
    build:
      context: .
      dockerfile: src/Services/Basket/Basket.API/Dockerfile
    depends_on:
      - sqldata

我的目標是使用 http 客戶端從BasketService.cs類中調用Catalog.API 您可能已經注意到,我嘗試使用localhost:80調用 Web Api,但這不起作用,因為兩個 API 都在不同的容器上運行,並且 localhost 僅對 Basket 容器有效(我有 BasketService 類)。 我不確定如何正確調用 Catalog.API。 我可以使用docker inspect <dockerid>手動查找容器的 IP,但是每次重新啟動或重建容器時,ip 都會更改。 所以這不是一個好方法。 使用服務名稱不起作用。

解決此問題的最佳方法是什么? 我應該使用哪種方式調用 Api,在另一個容器上運行?

您必須創建一個網絡來通信兩個容器

docker network create your-network docker network connect your-network the-service-container

然后,您可以編寫 _client.GetStringAsync("http://the-service-container:80/api/basket/entries");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM