![](/img/trans.png)
[英]Dockerize an ASP.NET Core Web App with MySQL persisting Database as two separate containers with docker-compose
[英]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.API
在localhost: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.