.NET 云原生架构师训练营(模块二 基础巩固 MongoDB API实现)--学习笔记

简介: - 问题查询单个实现- 问题查询列表实现- 问题跨集合查询实现- 问题创建实现- 问题更新实现- 问题回答实现- 问题评论实现- 问题投票实现- 回答实现

2.5.7 MongoDB -- API实现

  • 问题查询单个实现
  • 问题查询列表实现
  • 问题跨集合查询实现
  • 问题创建实现
  • 问题更新实现
  • 问题回答实现
  • 问题评论实现
  • 问题投票实现
  • 回答实现

QuestionController

namespace LighterApi.Controller
{
    [ApiController]
    [Route("api/[controller]")]
    public class QuestionController : ControllerBase
    {
        private readonly IMongoCollection<Question> _questionCollection;

        private readonly IMongoCollection<Vote> _voteCollection;

        private readonly IMongoCollection<Answer> _answerCollection;

        public QuestionController(IMongoClient mongoClient)
        {
            var database = mongoClient.GetDatabase("lighter");

            _questionCollection = database.GetCollection<Question>("questions");
            _voteCollection = database.GetCollection<Vote>("votes");
            _answerCollection = database.GetCollection<Answer>("answers");
        }
    }
}

问题查询单个实现

linq 查询

[HttpGet]
[Route("{id}")]
public async Task<ActionResult<Question>> GetAsync(string id, CancellationToken cancellationToken)
{
    var question = await _questionCollection.AsQueryable()
        .FirstOrDefaultAsync(q => q.Id == id, cancellationToken: cancellationToken);
    if (question == null)
        return NotFound();

    return Ok(question);
}

mongo 查询表达式

var filter = Builders<Question>.Filter.Eq(q => q.Id, id);
await _questionCollection.Find(filter).FirstOrDefaultAsync(cancellationToken);

构造空查询条件的表达式

var filter = string.IsNullOrEmpty(id)
    ? Builders<Question>.Filter.Empty
    : Builders<Question>.Filter.Eq(q => q.Id, id);

多段拼接 filter

var filter2 = Builders<Question>.Filter.And(filter, Builders<Question>.Filter.Eq(q => q.TenantId , "001"));

问题查询列表实现

  • 数据 AnyIn查询
  • 排序 sort : StirngFieldDefinition
  • 分页 skip, limit
[HttpGet]
public async Task<ActionResult<List<Question>>> GetListAsync([FromQuery] List<string> tags,
    CancellationToken cancellationToken, [FromQuery] string sort = "createdAt", [FromQuery] int skip = 0,
    [FromQuery] int limit = 10)
{
    //// linq 查询
    //await _questionCollection.AsQueryable().Where(q => q.ViewCount > 10)
    //    .ToListAsync(cancellationToken: cancellationToken);

    var filter = Builders<Question>.Filter.Empty;

    if (tags != null && tags.Any())
    {
        filter = Builders<Question>.Filter.AnyIn(q => q.Tags, tags);
    }

    var sortDefinition = Builders<Question>.Sort.Descending(new StringFieldDefinition<Question>(sort));

    var result = await _questionCollection
        .Find(filter)
        .Sort(sortDefinition)
        .Skip(skip)
        .Limit(limit)
        .ToListAsync(cancellationToken: cancellationToken);

    return Ok(result);
}

问题跨集合查询实现

[HttpGet]
[Route("{id}/answers")]
public async Task<ActionResult> GetWithAnswerAsync(string id, CancellationToken cancellationToken)
{
    // linq 查询
    var query = from question in _questionCollection.AsQueryable()
                where question.Id == id
                join a in _answerCollection.AsQueryable() on question.Id equals a.QuestionId into answers
                select new { question, answers };

    var result = await query.FirstOrDefaultAsync(cancellationToken);

    if (result == null)
        return NotFound();

    return Ok(result);
}

问题创建实现

[HttpPost]
public async Task<ActionResult<Question>> CreateAsync([FromBody] Question question,
    CancellationToken cancellationToken)
{
    question.Id = Guid.NewGuid().ToString();
    await _questionCollection.InsertOneAsync(question, new InsertOneOptions {BypassDocumentValidation = false},
        cancellationToken);
    return StatusCode((int) HttpStatusCode.Created, question);
}

问题更新实现

[HttpPatch]
[Route("{id}")]
public async Task<ActionResult> UpdateAsync([FromRoute] string id, [FromBody] QuestionUpdateRequest request,
    CancellationToken cancellationToken)
{
    if (string.IsNullOrEmpty(request.Summary))
        throw new ArgumentNullException(nameof(request.Summary));

    var filter = Builders<Question>.Filter.Eq(q => q.Id, id);

    var update = Builders<Question>.Update
        .Set(q => q.Title, request.Title)
        .Set(q => q.Content, request.Content)
        .Set(q => q.Tags, request.Tags)
        .Push(q => q.Comments, new Comment { Content = request.Summary, CreatedAt = DateTime.Now });

    await _questionCollection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);

    return Ok();
}

使用拼接的方式构建表达式

var updateFieldList = new List<UpdateDefinition<Question>>();

if (!string.IsNullOrWhiteSpace(request.Title))
    updateFieldList.Add(Builders<Question>.Update.Set(q => q.Title, request.Title));

if (!string.IsNullOrWhiteSpace(request.Content))
    updateFieldList.Add(Builders<Question>.Update.Set(q => q.Content, request.Content));

if (request.Tags != null && request.Tags.Any())
    updateFieldList.Add(Builders<Question>.Update.Set(q => q.Tags, request.Tags));

updateFieldList.Add(Builders<Question>.Update.Push(q => q.Comments,
    new Comment {Content = request.Summary, CreatedAt = DateTime.Now}));

var update = Builders<Question>.Update.Combine(updateFieldList);

问题回答实现

[HttpPost]
[Route("{id}/answer")]
public async Task<ActionResult<Answer>> AnswerAsync([FromRoute] string id, [FromBody] AnswerRequest request,
    CancellationToken cancellationToken)
{
    var answer = new Answer {QuestionId = id, Content = request.Content, Id = Guid.NewGuid().ToString()};
    _answerCollection.InsertOneAsync(answer, cancellationToken);

    var filter = Builders<Question>.Filter.Eq(q => q.Id, id);
    var update = Builders<Question>.Update.Push(q => q.Answers, answer.Id);

    await _questionCollection.UpdateOneAsync(filter, update, null, cancellationToken);

    return Ok();
}

问题评论实现

[HttpPost]
[Route("{id}/comment")]
public async Task<ActionResult> CommentAsync([FromRoute] string id, [FromBody] CommentRequest request,
    CancellationToken cancellationToken)
{
    var filter = Builders<Question>.Filter.Eq(q => q.Id, id);
    var update = Builders<Question>.Update.Push(q => q.Comments,
        new Comment {Content = request.Content, CreatedAt = DateTime.Now});

    await _questionCollection.UpdateOneAsync(filter, update, null, cancellationToken);

    return Ok();
}

问题投票实现

[HttpPost]
[Route("{id}/up")]
public async Task<ActionResult> UpAsync([FromBody] string id, CancellationToken cancellationToken)
{
    var vote = new Vote
    {
        Id = Guid.NewGuid().ToString(),
        SourceType = ConstVoteSourceType.Question,
        SourceId = id,
        Direction = EnumVoteDirection.Up
    };

    await _voteCollection.InsertOneAsync(vote, cancellationToken);

    var filter = Builders<Question>.Filter.Eq(q => q.Id, id);
    var update = Builders<Question>.Update.Inc(q => q.VoteCount, 1).AddToSet(q => q.VoteUps, vote.Id);
    await _questionCollection.UpdateOneAsync(filter, update);

    return Ok();
}

[HttpPost]
[Route("{id}/down")]
public async Task<ActionResult> DownAsync([FromBody] string id, CancellationToken cancellationToken)
{
    var vote = new Vote
    {
        Id = Guid.NewGuid().ToString(),
        SourceType = ConstVoteSourceType.Question,
        SourceId = id,
        Direction = EnumVoteDirection.Down
    };

    await _voteCollection.InsertOneAsync(vote, cancellationToken);

    var filter = Builders<Question>.Filter.Eq(q => q.Id, id);
    var update = Builders<Question>.Update.Inc(q => q.VoteCount, -1).AddToSet(q => q.VoteDowns, vote.Id);
    await _questionCollection.UpdateOneAsync(filter, update);

    return Ok();
}

回答实现

namespace LighterApi.Controller
{
    [ApiController]
    [Route("api/[controller]")]
    public class AnswerController : ControllerBase
    {
        private readonly IMongoCollection<Vote> _voteCollection;

        private readonly IMongoCollection<Answer> _answerCollection;

        public AnswerController(IMongoClient mongoClient)
        {
            var database = mongoClient.GetDatabase("lighter");

            _voteCollection = database.GetCollection<Vote>("votes");
            _answerCollection = database.GetCollection<Answer>("answers");
        }

        [HttpGet]
        public async Task<ActionResult<Answer>> GetListAsync([FromQuery] string questionId, CancellationToken cancellationToken)
        {
            var list = await _answerCollection.AsQueryable().Where(a => a.QuestionId == questionId)
                .ToListAsync(cancellationToken);
            return Ok(list);
        }

        [HttpPatch]
        [Route("{id}")]
        public async Task<ActionResult> UpdateAsync(string id, string content, string summary,
            CancellationToken cancellationToken)
        {
            var filter = Builders<Answer>.Filter.Eq(q => q.Id, id);

            var update = Builders<Answer>.Update
                .Set(q => q.Content, content)
                .Push(q => q.Comments, new Comment { Content = summary, CreatedAt = DateTime.Now });

            await _answerCollection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);

            return Ok();
        }

        [HttpPost]
        [Route("{id}/comment")]
        public async Task<ActionResult> CommentAsync([FromRoute] string id, [FromBody] CommentRequest request,
    CancellationToken cancellationToken)
        {
            var filter = Builders<Answer>.Filter.Eq(q => q.Id, id);
            var update = Builders<Answer>.Update.Push(q => q.Comments,
                new Comment { Content = request.Content, CreatedAt = DateTime.Now });

            await _answerCollection.UpdateOneAsync(filter, update, null, cancellationToken);

            return Ok();
        }

        [HttpPost]
        [Route("{id}/up")]
        public async Task<ActionResult> UpAsync([FromBody] string id, CancellationToken cancellationToken)
        {
            var vote = new Vote
            {
                Id = Guid.NewGuid().ToString(),
                SourceType = ConstVoteSourceType.Answer,
                SourceId = id,
                Direction = EnumVoteDirection.Up
            };

            await _voteCollection.InsertOneAsync(vote, cancellationToken);

            var filter = Builders<Answer>.Filter.Eq(q => q.Id, id);
            var update = Builders<Answer>.Update.Inc(q => q.VoteCount, 1).AddToSet(q => q.VoteUps, vote.Id);
            await _answerCollection.UpdateOneAsync(filter, update);

            return Ok();
        }

        [HttpPost]
        [Route("{id}/down")]
        public async Task<ActionResult> DownAsync([FromBody] string id, CancellationToken cancellationToken)
        {
            var vote = new Vote
            {
                Id = Guid.NewGuid().ToString(),
                SourceType = ConstVoteSourceType.Answer,
                SourceId = id,
                Direction = EnumVoteDirection.Down
            };

            await _voteCollection.InsertOneAsync(vote, cancellationToken);

            var filter = Builders<Answer>.Filter.Eq(q => q.Id, id);
            var update = Builders<Answer>.Update.Inc(q => q.VoteCount, -1).AddToSet(q => q.VoteDowns, vote.Id);
            await _answerCollection.UpdateOneAsync(filter, update);

            return Ok();
        }
    }
}

GitHub源码链接:

https://github.com/MINGSON666/Personal-Learning-Library/tree/main/ArchitectTrainingCamp/LighterApi

目录
相关文章
|
12月前
|
运维 NoSQL Serverless
|
Cloud Native API
微服务引擎 MSE 及云原生 API 网关 2025 年 6 月产品动态
微服务引擎 MSE 及云原生 API 网关 2025 年 6 月产品动态
|
安全 测试技术 API
电商API接口开发:基础架构搭建全攻略
本文详细解析了电商API接口从零搭建基础架构的全流程。首先通过需求分析明确业务功能与接口规范,选定数据格式(如JSON)及通信方式(如RESTful)。接着在架构设计阶段选择合适的技术栈、数据库方案,并引入API网关实现统一管理。开发实现部分涵盖认证授权、数据访问、日志记录与异常处理等核心功能。安全防护则强调数据加密、传输安全及速率限制策略。测试优化阶段包括单元测试、集成测试、性能与安全测试,确保接口稳定性。最后通过工具生成清晰的API文档并实施版本控制,为开发者提供便利。整体流程系统化、模块化,助力打造高效、安全的电商API接口。
|
Cloud Native API
微服务引擎 MSE 及云原生 API 网关 2025 年 3 月产品动态
微服务引擎 MSE 及云原生 API 网关 2025 年 3 月产品动态
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
614 102
|
10月前
|
人工智能 API 数据库
Semantic Kernel .NET 架构学习指南
本指南系统解析微软Semantic Kernel .NET架构,涵盖核心组件、设计模式与源码结构,结合实战路径与调试技巧,助你从入门到贡献开源,掌握AI编排开发全栈技能。
1127 2
|
10月前
|
存储 监控 安全
132_API部署:FastAPI与现代安全架构深度解析与LLM服务化最佳实践
在大语言模型(LLM)部署的最后一公里,API接口的设计与安全性直接决定了模型服务的可用性、稳定性与用户信任度。随着2025年LLM应用的爆炸式增长,如何构建高性能、高安全性的REST API成为开发者面临的核心挑战。FastAPI作为Python生态中最受青睐的Web框架之一,凭借其卓越的性能、强大的类型安全支持和完善的文档生成能力,已成为LLM服务化部署的首选方案。
1412 3
|
12月前
|
运维 NoSQL Serverless
《第四纪元》玩得轻松,构建也轻松 | 阿里云云原生 API 网关、函数计算助力 IGame 快速构建轻休闲游戏
在轻休闲游戏流量波动大、生命周期短的背景下,传统架构难以应对成本与扩展挑战。本文介绍了基于阿里云函数计算 FC 和 Redis 构建的新一代服务器架构,实现弹性伸缩、成本优化与高效运维,助力轻休闲游戏快速迭代与稳定运营,提升开发效率并降低运维复杂度。
《第四纪元》玩得轻松,构建也轻松 | 阿里云云原生 API 网关、函数计算助力 IGame 快速构建轻休闲游戏
|
运维 Prometheus 监控
云原生 API 网关 x OKG:游戏连接治理的「最后一公里」
本文介绍了云原生技术在游戏连接治理中的应用,重点探讨了如何通过 OpenKruiseGame(OKG)与云原生 API 网关的结合,实现游戏服务的优雅下线与无感配置变更。文章分析了游戏服务的强状态特性所带来的挑战,并提出了基于状态感知与连接管理的解决方案,保障玩家会话的连续性与体验的稳定性。同时,还介绍了如何通过零改造接入、全栈可观测性与简化的 API 治理,缩短游戏服务云原生化的“最后一公里”。
500 4

推荐镜像

更多