溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

發(fā)布時(shí)間:2021-12-27 16:55:48 來(lái)源:億速云 閱讀:211 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    需求

    需求很簡(jiǎn)單:如何創(chuàng)建新的TodoList和TodoItem并持久化。

    初學(xué)者按照教程去實(shí)現(xiàn)的話,應(yīng)該分成以下幾步:創(chuàng)建Controller并實(shí)現(xiàn)POST方法;實(shí)用傳入的請(qǐng)求參數(shù)new一個(gè)數(shù)據(jù)庫(kù)實(shí)體對(duì)象;調(diào)用IRepository<T>完成數(shù)據(jù)庫(kù)的寫入,最多會(huì)在中間加一層Service。這個(gè)做法本身沒(méi)有問(wèn)題,也是需要從初學(xué)階段開始扎實(shí)地掌握開發(fā)技能的必經(jīng)之路,有助于幫助理解邏輯調(diào)用的過(guò)程。

    對(duì)于稍微正式一些的項(xiàng)目,.NET工程上習(xí)慣的實(shí)現(xiàn)是通過(guò)使用一些比較成熟的類庫(kù)框架,有效地對(duì)業(yè)務(wù)邏輯進(jìn)行分類管理、消除冗余代碼,以達(dá)到業(yè)務(wù)邏輯職責(zé)清晰簡(jiǎn)潔的目的。在這個(gè)階段我們經(jīng)常使用的兩個(gè)類庫(kù)分別是AutoMapper和MediatR。

    目標(biāo)

    合理組織并使用MediatR,完成POST請(qǐng)求。

    原理與思路

    首先來(lái)簡(jiǎn)單地介紹一下這個(gè)類庫(kù)。

    關(guān)于CQRS模式、中介者模式和MediatR

    CQRS模式

    CQRS模式全稱是“Command Query Responsibility Segregation”,正如字面意思,CQRS模式的目的在于將讀取操作和寫入操作的指責(zé)區(qū)分開,并使用不同的Model去表示。從CRUD的角度來(lái)說(shuō),就是把R和CUD區(qū)分開來(lái)對(duì)待。如下圖所示:

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    這個(gè)模式可以有效地應(yīng)用到具有主從分離的數(shù)據(jù)庫(kù)架構(gòu)中,當(dāng)需要獲取數(shù)據(jù)時(shí),從只讀數(shù)據(jù)庫(kù)(一般是從庫(kù))中讀取數(shù)據(jù),當(dāng)需要寫入或更新數(shù)據(jù)時(shí),向主庫(kù)進(jìn)行操作。

    CQRS模式旨在解決的問(wèn)題是:為了屏蔽數(shù)據(jù)庫(kù)層面“寫優(yōu)先”還是“讀優(yōu)先”的優(yōu)化設(shè)計(jì)策略,在業(yè)務(wù)邏輯側(cè)進(jìn)行解耦。

    任何設(shè)計(jì)模式都是對(duì)解決特定問(wèn)題的一個(gè)Trade off,自然也帶來(lái)了一些缺點(diǎn),首先就是服務(wù)內(nèi)部的組件復(fù)雜度上升了,因?yàn)樾枰獎(jiǎng)?chuàng)建額外的類來(lái)實(shí)現(xiàn)CQRS模式;其次如果數(shù)據(jù)層是分離的,那么可能會(huì)有數(shù)據(jù)的狀態(tài)不一致問(wèn)題。

    中介者M(jìn)ediator模式

    這是23種基本設(shè)計(jì)模式中的一個(gè),屬于行為型設(shè)計(jì)模式,它給出了組件之間交互的一種解耦的方式。簡(jiǎn)單參考下圖,具體內(nèi)容就不過(guò)多解釋了,任何一篇介紹設(shè)計(jì)模式的文章都有介紹。

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    這種設(shè)計(jì)模式實(shí)際上是一種采用依賴倒置(Inversion of Control, IoC)的方式,實(shí)現(xiàn)了圖中藍(lán)色組件的松耦合。

    MediatR

    這是在開發(fā)中被廣泛采用的實(shí)現(xiàn)以上兩種設(shè)計(jì)模式的類庫(kù),更準(zhǔn)確的說(shuō)法是,它通過(guò)應(yīng)用中介者模式,實(shí)現(xiàn)了進(jìn)程內(nèi)CQRS?;舅枷胧撬衼?lái)自API接口和數(shù)據(jù)存儲(chǔ)之間的邏輯,都需要通過(guò)MediatR來(lái)組織(即所謂的“中介者”)。

    從實(shí)現(xiàn)上看,MediatR提供了幾組用于不同場(chǎng)景的接口,我們?cè)诒疚闹刑幚淼谋容^多的是IRequest<T>/IRequestHandler<T>以及INotification<T>/INotificationHander<T>兩組接口,更多的請(qǐng)參考官方文檔和例子。

    實(shí)現(xiàn)

    所有需要使用MediatR的地方都集中在Application項(xiàng)目中。

    引入MediatR

    $ dotnet add src/TodoList.Application/TodoList.Application.csproj package MediatR.Extensions.Microsoft.DependencyInjection

    為了適配CQRS的模式,我們?cè)贏pplication項(xiàng)目中的TodoLists和TodoItems下相同地創(chuàng)建幾個(gè)文件夾:

    Commands:用于組織CUD相關(guān)的業(yè)務(wù)邏輯;

    Queries:用于組織R相關(guān)的業(yè)務(wù)邏輯;

    EventHandlers:用于組織領(lǐng)域事件處理的相關(guān)業(yè)務(wù)邏輯。

    在Application根目錄下同樣創(chuàng)建DependencyInjection.cs用于該項(xiàng)目的依賴注入:

    DependencyInjection.cs

    using System.Reflection;
    
    using Microsoft.Extensions.DependencyInjection;
    
    
    
    namespace TodoList.Application;
    
    
    
    public static class DependencyInjection
    
    {
    
        public static IServiceCollection AddApplication(this IServiceCollection services)
    
        {
    
            services.AddMediatR(Assembly.GetExecutingAssembly());
    
            return services;
    
        }
    
    }

    并在Api項(xiàng)目中使用:

    // 省略其他...
    // 添加應(yīng)用層配置
    builder.Services.AddApplication();
    // 添加基礎(chǔ)設(shè)施配置
    builder.Services.AddInfrastructure(builder.Configuration);

    實(shí)現(xiàn)Post請(qǐng)求

    在本章中我們只實(shí)現(xiàn)TodoList和TodoItem的Create接口(POST),剩下的接口后面的文章中逐步涉及。

    POST TodoList

    在Application/TodoLists/Commands/下新建一個(gè)目錄CreateTodoList用于存放創(chuàng)建一個(gè)TodoList相關(guān)的所有邏輯:

    CreateTodoListCommand.cs

    using MediatR;
    
    using TodoList.Application.Common.Interfaces;
    
    
    
    namespace TodoList.Application.TodoLists.Commands.CreateTodoList;
    
    
    
    public class CreateTodoListCommand : IRequest<Guid>
    
    {
    
        public string? Title { get; set; }
    
    }
    
    
    
    public class CreateTodoListCommandHandler : IRequestHandler<CreateTodoListCommand, Guid>
    
    {
    
        private readonly IRepository<Domain.Entities.TodoList> _repository;
    
    
    
        public CreateTodoListCommandHandler(IRepository<Domain.Entities.TodoList> repository)
    
        {
    
            _repository = repository;
    
        }
    
    
    
        public async Task<Guid> Handle(CreateTodoListCommand request, CancellationToken cancellationToken)
    
        {
    
            var entity = new Domain.Entities.TodoList
    
            {
    
                Title = request.Title
    
            };
    
    
    
            await _repository.AddAsync(entity, cancellationToken);
    
            return entity.Id;
    
        }
    
    }

    有一些實(shí)踐是將Request和RequestHandler分開兩個(gè)文件,我更傾向于像這樣將他倆放在一起,一是保持簡(jiǎn)潔,二是當(dāng)你需要順著一個(gè)Command去尋找它對(duì)應(yīng)的Handler時(shí),不需要更多的跳轉(zhuǎn)。

    接下來(lái)在TodoListController里實(shí)現(xiàn)對(duì)應(yīng)的POST方法,

    using MediatR;
    
    using Microsoft.AspNetCore.Mvc;
    
    using TodoList.Application.TodoLists.Commands.CreateTodoList;
    
    
    
    namespace TodoList.Api.Controllers;
    
    
    
    [ApiController]
    
    [Route("/todo-list")]
    
    public class TodoListController : ControllerBase
    
    {
    
        private readonly IMediator _mediator;
    
    
    
        // 注入MediatR
    
        public TodoListController(IMediator mediator)
    
            => _mediator = mediator;
    
    
    
        [HttpPost]
    
        public async Task<Guid> Create([FromBody] CreateTodoListCommand command)
    
        {
    
            var createdTodoList = await _mediator.Send(command);
    
    
    
            // 出于演示的目的,這里只返回創(chuàng)建出來(lái)的TodoList的Id,
    
            // 實(shí)際使用中可能會(huì)選擇IActionResult作為返回的類型并返回CreatedAtRoute對(duì)象,
    
            // 因?yàn)槲覀冞€沒(méi)有去寫GET方法,返回CreatedAtRoute會(huì)報(bào)錯(cuò)(找不到對(duì)應(yīng)的Route),等講完GET后會(huì)在那里更新
    
            return createdTodoList.Id;
    
        }
    
    }

    POST TodoItem

    類似TodoListController和CreateTodoListCommand的實(shí)現(xiàn),這里我直接把代碼貼出來(lái)了。

    CreateTodoItemCommand.cs

    using MediatR;
    
    using TodoList.Application.Common.Interfaces;
    
    using TodoList.Domain.Entities;
    
    using TodoList.Domain.Events;
    
    
    
    namespace TodoList.Application.TodoItems.Commands.CreateTodoItem;
    
    
    
    public class CreateTodoItemCommand : IRequest<Guid>
    
    {
    
        public Guid ListId { get; set; }
    
    
    
        public string? Title { get; set; }
    
    }
    
    
    
    public class CreateTodoItemCommandHandler : IRequestHandler<CreateTodoItemCommand, Guid>
    
    {
    
        private readonly IRepository<TodoItem> _repository;
    
    
    
        public CreateTodoItemCommandHandler(IRepository<TodoItem> repository)
    
        {
    
            _repository = repository;
    
        }
    
    
    
        public async Task<Guid> Handle(CreateTodoItemCommand request, CancellationToken cancellationToken)
    
        {
    
            var entity = new TodoItem
    
            {
    
                // 這個(gè)ListId在前文中的代碼里漏掉了,需要添加到Domain.Entities.TodoItem實(shí)體上
    
                ListId = request.ListId,
    
                Title = request.Title,
    
                Done = false
    
            };
    
    
    
            await _repository.AddAsync(entity, cancellationToken);
    
    
    
            return entity.Id;
    
        }
    
    }

    TodoItemController.cs

    using MediatR;
    
    using Microsoft.AspNetCore.Mvc;
    
    using TodoList.Application.TodoItems.Commands.CreateTodoItem;
    
    
    
    namespace TodoList.Api.Controllers;
    
    
    
    [ApiController]
    
    [Route("/todo-item")]
    
    public class TodoItemController : ControllerBase
    
    {
    
        private readonly IMediator _mediator;
    
    
    
        // 注入MediatR
    
        public TodoItemController(IMediator mediator) 
    
            => _mediator = mediator;
    
    
    
        [HttpPost]
    
        public async Task<Guid> Create([FromBody] CreateTodoItemCommand command)
    
        {
    
            var createdTodoItem = await _mediator.Send(command);
    
    
    
            // 處于演示的目的,這里只返回創(chuàng)建出來(lái)的TodoItem的Id,理由同前
    
            return createdTodoItem.Id;
    
        }
    
    }

    驗(yàn)證

    運(yùn)行Api項(xiàng)目,通過(guò)Hoppscotch發(fā)送對(duì)應(yīng)接口請(qǐng)求:

    創(chuàng)建TodoList驗(yàn)證

    請(qǐng)求

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    返回

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    數(shù)據(jù)庫(kù)

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    第一條數(shù)據(jù)是種子數(shù)據(jù),第二條是我們剛才創(chuàng)建的。

    創(chuàng)建TodoItem驗(yàn)證

    繼續(xù)拿剛才創(chuàng)建的這個(gè)TodoList的Id來(lái)創(chuàng)建新的TodoItem:

    請(qǐng)求

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    返回

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    數(shù)據(jù)庫(kù)

    如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求

    最后一條是我們新創(chuàng)建的,其余是種子數(shù)據(jù)。

    “如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI