.Net微服务,一步一步从零开始搭建 .Net6 + MongoDB + Docker Compose

我的开发环境:VS Code、Docker Desktop,都是目前的最新版本。

1.创建RESTful API

首先,我们新建一个文件夹,并用vs code打开该文件夹(我的文件夹名称叫"dotnet_docker")。
然后我们打开shell命令行,输入如下命令:

dotnet new webapi -o StudentApi
cd StudentApi

创建一个名为"StudentApi"的webapi项目,并切换到该项目的目录下。
如图1:

图1

添加mongodb驱动,输入如下命令:

dotnet add package MongoDB.Driver
  • 添加model
    创建一个文件夹名为"Models",然后在里面添加一个"Student"的class,代码如下:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace StudentApi.Models;

public class Student
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string? Id { get; set; }

    [BsonElement("name")]
    public string Name { get; set; } = null!;

    [BsonElement("age")]
    public int Age { get; set; }
}
  • 添加数据库配置
    修改开发环境配置文件appsettings.Development.json,添加"StudentStoreDatabase"节点,代码如下:
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "StudentStoreDatabase": {
    "ConnectionString": "mongodb://localhost:27017",
    "DatabaseName": "StudentStore",
    "StudentsCollectionName": "Students"
  }
}

在"Models"文件夹添加一个名为"StudentStoreDatabaseSettings"的model用来映射"StudentStoreDatabase节点,代码如下:

namespace StudentApi.Models;

public class StudentStoreDatabaseSettings
{
    public string ConnectionString { get; set; } = null!;

    public string DatabaseName { get; set; } = null!;

    public string StudentsCollectionName { get; set; } = null!;
}
  • 在Program.cs文件中,添加数据库映射
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.Configure<StudentStoreDatabaseSettings>(
    builder.Configuration.GetSection("StudentStoreDatabase")); //添加数据库映射

在最顶部添加StudentStoreDatabaseSettings的引用

using StudentApi.Models;
  • 添加增删改查CRUD service
    创建一个名为"Services"文件夹,并往里面添加"StudentsService"的class
using StudentApi.Models;
using Microsoft.Extensions.Options;
using MongoDB.Driver;

namespace StudentApi.Services;

public class StudentsService
{
    private readonly IMongoCollection<Student> _studentsCollection;

    public StudentsService(
        IOptions<StudentStoreDatabaseSettings> studentStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            studentStoreDatabaseSettings.Value.ConnectionString);

        var mongoDatabase = mongoClient.GetDatabase(
            studentStoreDatabaseSettings.Value.DatabaseName);

        _studentsCollection = mongoDatabase.GetCollection<Student>(
            studentStoreDatabaseSettings.Value.StudentsCollectionName);
    }

    public async Task<List<Student>> GetAsync() =>
        await _studentsCollection.Find(_ => true).ToListAsync();

    public async Task<Student?> GetAsync(string id) =>
        await _studentsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();

    public async Task CreateAsync(Student newStudent) =>
        await _studentsCollection.InsertOneAsync(newStudent);

    public async Task UpdateAsync(string id, Student updatedStudent) =>
        await _studentsCollection.ReplaceOneAsync(x => x.Id == id, updatedStudent);

    public async Task RemoveAsync(string id) =>
        await _studentsCollection.DeleteOneAsync(x => x.Id == id);
}

  • 添加单例模式依赖(singleton)
    这里用singleton而不是像之前的EF那样用scoped,因为根据官网文档它是直接依赖MongoClient。
    在Program.cs添加如下代码代码:
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.Configure<StudentStoreDatabaseSettings>(
    builder.Configuration.GetSection("StudentStoreDatabase"));//添加数据库映射
builder.Services.AddSingleton<StudentsService>(); //添加单例模式依赖(singleton)
  • 添加controller
    在Controllers文件夹添加""的class文件:
using StudentApi.Models;
using StudentApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace StudentApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class StudentsController : ControllerBase
{
    private readonly StudentsService _studentsService;

    public StudentsController(StudentsService studentsService) =>
        _studentsService = studentsService;

    [HttpGet]
    public async Task<List<Student>> Get() =>
        await _studentsService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Student>> Get(string id)
    {
        var student = await _studentsService.GetAsync(id);

        if (student is null)
        {
            return NotFound();
        }

        return student;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Student newStudent)
    {
        await _studentsService.CreateAsync(newStudent);

        return CreatedAtAction(nameof(Get), new { id = newStudent.Id }, newStudent);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Student updatedStudent)
    {
        var student = await _studentsService.GetAsync(id);

        if (student is null)
        {
            return NotFound();
        }

        updatedStudent.Id = student.Id;

        await _studentsService.UpdateAsync(id, updatedStudent);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var student = await _studentsService.GetAsync(id);

        if (student is null)
        {
            return NotFound();
        }

        await _studentsService.RemoveAsync(id);

        return NoContent();
    }
}

另外,需要把https跳转关了,因为我们本地没有验证证书。
在Program.cs文件中,在注释"app.UseHttpsRedirection();"

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//注释这里
// app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

至此,整体目录结构应该如图2:


图2
  • 测试web API
    打开shell命令行,输入如下命令运行项目:
 dotnet run

运行成功应该会如图3所示:


图3
  • 测试API
    这边我是用Postman来测试请求:
    图4,post添加数据:


    图4

图5,get请求所有数据:


图5

2.创建Dockerfile

Dockerfile就是把你目前的项目打包成已经镜像,然后用这个镜像进行部署。
我们先在shell命令行按 "control+c" 把运行中的项目停了,然后在项目目录(StudentApi)中添加一个名为"Dockerfile"的文件,注意不要加任何后续,如"txt"这些,并添加如下代码:

#第一阶段,利用sdk镜像环境生成发布dll,主要是运行"dotnet publish"这个命令;
#这个阶段也可以在外面用shell直接运行。
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-evn
WORKDIR /source

COPY . ./
RUN dotnet restore
RUN dotnet publish -c Release -o /out

#第二阶段,利用aspnet镜像运行第一阶段生成发布的dll,主要是"dotnet xxx.dll"这个命令,跟我们在linux运行.net一样
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app

COPY --from=build-evn /out .
ENTRYPOINT ["dotnet","StudentApi.dll"]

3.创建docker-compose.yml

有时候我们的docker run命令会很长很长,每次部署服务器时候都写一次十分不现实, docker-compose可以把你docker run命令写到一个文件中,而且默认是用同一个docker network,假如你安装的是docker desktop,则不需要额外安装docker-compose,如果你是linux服务器,除了安装docker engine外,还要自己手动安装docker-compose。
在项目目录(StudentApi)中添加一个名为"docker-compose.yml"的文件,并添加如下代码:

version: '3.6'
services:
  web:
    build: . #根目录下会找到我们之前的Dockerfile,并运行生成镜像
    ports:
      - "8080:80" #在docker容器中.net默认是80端口,可以通过environment指定
    depends_on:
      - db
  
  db: #这里名称会默认为网络的别名,这样我们web就可以不需要知道mongodb的ip,直接用这个名称代替ip
    image: mongo #我这里的mongodb没有指定用户密码,可以通过environment指定
    

注意,我这里没有用 volume,所以容器一旦关掉了数据都丢失了,我这里不是服务器,只是演示,所以我希望数据用完就丢失,假如有需要的同学可以加个volume

4.修改数据库连接

在1步中,我们用来连接数据的配置文件是"appsettings.Development.json",这文件在开发环境生效,
现在我们需要在"appsettings.json"添加mongodb的配置,添加"StudentStoreDatabase"节点,代码如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "StudentStoreDatabase": {
    "ConnectionString": "mongodb://db:27017",
    "DatabaseName": "StudentStore",
    "StudentsCollectionName": "Students"
  }
}

这里注意ConnectionString,在"appsettings.Development.json"中是"localhost",现在我们改成"db",db这个名字在"docker-compose.yml"文件中取。

5.打包镜像并运行

打开shell命令行,输入如下命令运行打包镜像并运行:

docker-compose up -d

运行成功应该会如图6:


图6

我们ps一下看看当前运行的镜像(图7):


图7

这两个就是我们刚才运行的运行的镜像。

6.测试

我们刚才在docker-compose.yml指定了映射8080端口,先在在Postman访问http://localhost:8080/api/students,get一下所有数据,如图7:

image.png

然后我们随便post加一条数据,如图8:
图8

我们再回过来get所有数据,如图9:
图9

进入mongo容器查看一下我们刚才数据:
在shell命令行ps一下拿到id并exec进去,并连接mongodb,如图10:
图10

找到我们创建的数据库,集,并查看数据,如图11:
图11

再一次提醒,这里数据因为我没有在docker-compose.yml加volume,所以容器关了就会丢失。

至此,完成。

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容