基於領域驅動設計(DDD)超輕量級快速開發架構

来源:https://www.cnblogs.com/neozhu/archive/2020/06/22/13174234.html
-Advertisement-
Play Games

smartadmin.core.urf 這個項目是基於asp.net core 3.1(最新)基礎上參照領域驅動設計(DDD)的理念,並參考目前最為了流行的abp架構開發的一套輕量級的快速開發web application 技術架構,專註業務核心需求,減少重覆代碼,開始構建和發佈,讓初級程式員也能開 ...


 

smartadmin.core.urf 這個項目是基於asp.net core 3.1(最新)基礎上參照領域驅動設計(DDD)的理念,並參考目前最為了流行的abp架構開發的一套輕量級的快速開發web application 技術架構,專註業務核心需求,減少重覆代碼,開始構建和發佈,讓初級程式員也能開發出專業並且漂亮的Web應用程式

域驅動設計(DDD)是一種通過將實現與不斷發展的模型相連接來滿足複雜需求的軟體開發方法。域驅動設計的前提如下:

  • 將項目的主要重點放在核心領域和領域邏輯上;
  • 將複雜的設計基於領域模型;
  • 啟動技術專家和領域專家之間的創造性合作,以迭代方式完善解決特定領域問題的概念模型。

最終的核心思想還是SOLID,只是實現的方式有所不同,ABP可能目前對DDD設計理念最好的實現方式。但對於小項目我還是更喜歡 URF.Core https://github.com/urfnet/URF.Core 這個超輕量級的實現。

同時這個項目也就是我2年前的一個開源項目 ASP.NET MVC 5 SmartCode Scaffolding for Visual Studio.Net 的升級版,支持.net core.目前沒有把所有功能都遷移到.net core,其中最重要的就是代碼生成這塊。再接下來的時間里主要就是完善代碼生成的插件。當然也要看是否受歡迎,如果反應一般,我可能不會繼續更新。

Demo 網站

 演示站點 
賬號:demo 密碼:123456

GitHub 源代碼 https://github.com/neozhu/smartadmin.core.urf

喜歡請給個 Star 每一顆Star都是鼓勵我繼續更新的動力 謝謝
如果你用於自己公司及盈利性的項目,希望給與金錢上的贊助,並且保留原作者的版權

分層

smartadmin.core.urf遵行DDD設計模式來實現應用程式的四層模型

  • 表示層(Presentation Layer):用戶操作展示界面,使用SmartAdmin - Responsive WebApp模板+Jquery EasyUI
  • 應用層(Application Layer):在表示層與域層之間,實現具體應用程式邏輯,業務用例,Project:StartAdmin.Service.csproj
  • 域層(Domain Layer):包括業務對象(Entity)和核心(域)業務規則,應用程式的核心,使用EntityFrmework Core Code-first + Repository實現
  • 基礎結構層(Infrastructure Layer):提供通用技術功能,這些功能主要有第三方庫來支持,比如日誌:Nlog,服務發現:Swagger UI,事件匯流排(EventBus):dotnetcore/CAP,認證與授權:Microsoft.AspNetCore.Identity,後面會具體介紹

內容

 

域層(Domain Layer)

  • 實體(Entity,BaseEntity) 通常實體就是映射到關係資料庫中的表,這裡說名一下最佳做法和慣例:
  1. 在域層定義:本項目就是(SmartAdmin.Entity.csproj)
  2. 繼承一個基類 Entity,添加必要審計類比如:創建時間,最後修改時間等
  3. 必須要有一個主鍵最好是GRUID(不推薦複合主鍵),但本項目使用遞增的int類型
  4. 欄位不要過多的冗餘,可以通過定義關聯關係
  5. 欄位屬性和方法儘量使用virtual關鍵字。有些ORM和動態代理工具需要

 

  • 存儲庫(Repositories) 封裝基本數據操作方法(CRUD),本項目應用 URF.Core實現
  • 域服務
  • 技術指標
  • 應用層

    • 應用服務:用於實現應用程式的用例。它們用於將域邏輯公開給表示層,從表示層(可選)使用DTO(數據傳輸對象)作為參數調用應用程式服務。它使用域對象執行某些特定的業務邏輯,並(可選)將DTO返回到表示層。因此,表示層與域層完全隔離。對應本項目:(SmartAdmin.Service.csproj)
    • 數據傳輸對象(DTO):用於在應用程式層和表示層或其他類型的客戶端之間傳輸數據,通常,使用DTO作為參數從表示層(可選)調用應用程式服務。它使用域對象執行某些特定的業務邏輯,並(可選)將DTO返回到表示層。因此,表示層與域層完全隔離.對應本項目:(SmartAdmin.Dto.csproj)
    • Unit of work:管理和控制應用程式中操作資料庫連接和事務 ,本項目使用 URF.Core實現
  • 基礎服務層

    • UI樣式定義:根據用戶喜好選擇多種頁面顯示模式
    • 租戶管理:使用EntityFrmework Core提供的Global Filter實現簡單多租戶應用
    • 賬號管理: 對登錄系統賬號維護,註冊,註銷,鎖定,解鎖,重置密碼,導入、導出等功能
    • 角色管理:使用Microsoft身份庫管理角色,用戶及其許可權管理
    • 導航菜單:系統主導航欄配置
    • 角色授權:配置角色顯示的菜單
    • 鍵值對配置:常用的數據字典維護,如何正確使用和想法後面會介紹
    • 導入&導出配置:使用Excel導入導出做一個可配置的功能
    • 系統日誌:asp.net core 自帶的日誌+Nlog把所有日誌保存到資料庫方便查詢和分析
    • 消息訂閱:集中訂閱CAP分散式事件匯流排的消息
    • WebApi: Swagger UI Api服務發現和線上調試工具
    • CAP: CAP看板查看發佈和訂閱的消息

快速上手開發

  • 開發環境
    • Visual Studio .Net 2019
    • .Net Core 3.1
    • Sql Server(LocalDb)
  • 附加資料庫

    使用SQL Server Management Studio 附加.\src\SmartAdmin.Data\db\smartadmindb.mdf 資料庫(如果是localdb,那麼不需要修改資料庫連接配置)

  • 打開解決方案

第一個簡單的需求開始 
新增 Company 企業信息 完成CRUD 導入導出功能

  • 新建實體對象(Entity)

在SmartAdmin.Entity.csproj項目的Models目錄下新增一個Company.cs類

 1 //記住:定義實體對象最佳做法,繼承基類,使用virtual關鍵字,儘可能的定義每個屬性,名稱,類型,長度,校驗規則,索引,預設值等
 2 namespace SmartAdmin.Data.Models
 3 {
 4     public partial class Company : URF.Core.EF.Trackable.Entity
 5     {
 6         [Display(Name = "企業名稱", Description = "歸屬企業名稱")]
 7         [MaxLength(50)]
 8         [Required]
 9         //[Index(IsUnique = true)]
10         public virtual string Name { get; set; }
11         [Display(Name = "組織代碼", Description = "組織代碼")]
12         [MaxLength(12)]
13         //[Index(IsUnique = true)]
14         [Required]
15         public virtual string Code { get; set; }
16         [Display(Name = "地址", Description = "地址")]
17         [MaxLength(128)]
18         [DefaultValue("-")]
19         public virtual string Address { get; set; }
20         [Display(Name = "聯繫人", Description = "聯繫人")]
21         [MaxLength(12)]
22         public virtual string Contect { get; set; }
23         [Display(Name = "聯繫電話", Description = "聯繫電話")]
24         [MaxLength(20)]
25         public virtual string PhoneNumber { get; set; }
26         [Display(Name = "註冊日期", Description = "註冊日期")]
27         [DefaultValue("now")]
28         public virtual  DateTime RegisterDate { get; set; }
29     }
30 }
31 //在 SmartAdmin.Data.csproj 項目 SmartDbContext.cs 添加
32 public virtual DbSet<Company> Companies { get; set; }
View Code
  • 添加服務對象 Service

在項目 SmartAdmin.Service.csproj 中添加ICompanyService.cs,CompanyService.cs 就是用來實現業務需求 用例的地方

  1 //ICompany.cs
  2 //根據實際業務用例來創建方法,預設的CRUD,增刪改查不需要再定義
  3 namespace SmartAdmin.Service
  4 {
  5   // Example: extending IService<TEntity> and/or ITrackableRepository<TEntity>, scope: ICustomerService
  6   public interface ICompanyService : IService<Company>
  7   {
  8     // Example: adding synchronous Single method, scope: ICustomerService
  9     Company Single(Expression<Func<Company, bool>> predicate);
 10     Task ImportDataTableAsync(DataTable datatable);
 11     Task<Stream> ExportExcelAsync(string filterRules = "", string sort = "Id", string order = "asc");
 12   }
 13 }
 14 // 具體實現介面的方法
 15 namespace SmartAdmin.Service
 16 {
 17   public class CompanyService : Service<Company>, ICompanyService
 18   {
 19     private readonly IDataTableImportMappingService mappingservice;
 20     private readonly ILogger<CompanyService> logger;
 21     public CompanyService(
 22       IDataTableImportMappingService mappingservice,
 23       ILogger<CompanyService> logger,
 24       ITrackableRepository<Company> repository) : base(repository)
 25     {
 26       this.mappingservice = mappingservice;
 27       this.logger = logger;
 28     }
 29 
 30     public async Task<Stream> ExportExcelAsync(string filterRules = "", string sort = "Id", string order = "asc")
 31     {
 32       var filters = PredicateBuilder.FromFilter<Company>(filterRules);
 33       var expcolopts = await this.mappingservice.Queryable()
 34              .Where(x => x.EntitySetName == "Company")
 35              .Select(x => new ExpColumnOpts()
 36              {
 37                EntitySetName = x.EntitySetName,
 38                FieldName = x.FieldName,
 39                IgnoredColumn = x.IgnoredColumn,
 40                SourceFieldName = x.SourceFieldName
 41              }).ToArrayAsync();
 42 
 43       var works = (await this.Query(filters).OrderBy(n => n.OrderBy(sort, order)).SelectAsync()).ToList();
 44       var datarows = works.Select(n => new
 45       {
 46         Id = n.Id,
 47         Name = n.Name,
 48         Code = n.Code,
 49         Address = n.Address,
 50         Contect = n.Contect,
 51         PhoneNumber = n.PhoneNumber,
 52         RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
 53       }).ToList();
 54       return await NPOIHelper.ExportExcelAsync("Company", datarows, expcolopts);
 55     }
 56 
 57     public async Task ImportDataTableAsync(DataTable datatable)
 58     {
 59       var mapping = await this.mappingservice.Queryable()
 60                         .Where(x => x.EntitySetName == "Company" &&
 61                            (x.IsEnabled == true || (x.IsEnabled == false && x.DefaultValue != null))
 62                            ).ToListAsync();
 63       if (mapping.Count == 0)
 64       {
 65         throw new  NullReferenceException("沒有找到Work對象的Excel導入配置信息,請執行[系統管理/Excel導入配置]");
 66       }
 67       foreach (DataRow row in datatable.Rows)
 68       {
 69 
 70         var requiredfield = mapping.Where(x => x.IsRequired == true && x.IsEnabled == true && x.DefaultValue == null).FirstOrDefault()?.SourceFieldName;
 71         if (requiredfield != null || !row.IsNull(requiredfield))
 72         {
 73           var item = new Company();
 74           foreach (var field in mapping)
 75           {
 76             var defval = field.DefaultValue;
 77             var contain = datatable.Columns.Contains(field.SourceFieldName ?? "");
 78             if (contain && !row.IsNull(field.SourceFieldName))
 79             {
 80               var worktype = item.GetType();
 81               var propertyInfo = worktype.GetProperty(field.FieldName);
 82               var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
 83               var safeValue = (row[field.SourceFieldName] == null) ? null : Convert.ChangeType(row[field.SourceFieldName], safetype);
 84               propertyInfo.SetValue(item, safeValue, null);
 85             }
 86             else if (!string.IsNullOrEmpty(defval))
 87             {
 88               var worktype = item.GetType();
 89               var propertyInfo = worktype.GetProperty(field.FieldName);
 90               if (string.Equals(defval, "now", StringComparison.OrdinalIgnoreCase) && (propertyInfo.PropertyType == typeof(DateTime) || propertyInfo.PropertyType == typeof(Nullable<DateTime>)))
 91               {
 92                 var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
 93                 var safeValue = Convert.ChangeType(DateTime.Now, safetype);
 94                 propertyInfo.SetValue(item, safeValue, null);
 95               }
 96               else if (string.Equals(defval, "guid", StringComparison.OrdinalIgnoreCase))
 97               {
 98                 propertyInfo.SetValue(item, Guid.NewGuid().ToString(), null);
 99               }
100               else if (string.Equals(defval, "user", StringComparison.OrdinalIgnoreCase))
101               {
102                 propertyInfo.SetValue(item, "", null);
103               }
104               else
105               {
106                 var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
107                 var safeValue = Convert.ChangeType(defval, safetype);
108                 propertyInfo.SetValue(item, safeValue, null);
109               }
110             }
111           }
112           this.Insert(item);
113         }
114       }
115     }
116 
117     // Example, adding synchronous Single method
118     public Company Single(Expression<Func<Company, bool>> predicate)
119     {
120       
121       return this.Repository.Queryable().Single(predicate);
122 
123     }
124   }
125 }
View Code
  • 添加Controller

MVC Controller

  1 namespace SmartAdmin.WebUI.Controllers
  2 {
  3   public class CompaniesController : Controller
  4   {
  5     private  readonly ICompanyService companyService;
  6     private readonly IUnitOfWork unitOfWork;
  7     private readonly ILogger<CompaniesController> _logger;
  8     private readonly IWebHostEnvironment _webHostEnvironment;
  9     public CompaniesController(ICompanyService companyService,
 10           IUnitOfWork unitOfWork,
 11           IWebHostEnvironment webHostEnvironment,
 12           ILogger<CompaniesController> logger)
 13     {
 14       this.companyService = companyService;
 15       this.unitOfWork = unitOfWork;
 16       this._logger = logger;
 17       this._webHostEnvironment = webHostEnvironment;
 18     }
 19 
 20     // GET: Companies
 21     public IActionResult Index()=> View();
 22     //datagrid 數據源
 23     public async Task<JsonResult> GetData(int page = 1, int rows = 10, string sort = "Id", string order = "asc", string filterRules = "")
 24     {
 25       try
 26       {
 27         var filters = PredicateBuilder.FromFilter<Company>(filterRules);
 28         var total = await this.companyService
 29                              .Query(filters)
 30                              .AsNoTracking()
 31                              .CountAsync()
 32                               ;
 33         var pagerows = (await this.companyService
 34                              .Query(filters)
 35                               .AsNoTracking()
 36                            .OrderBy(n => n.OrderBy(sort, order))
 37                            .Skip(page - 1).Take(rows)
 38                            .SelectAsync())
 39                            .Select(n => new
 40                            {
 41                              Id = n.Id,
 42                              Name = n.Name,
 43                              Code = n.Code,
 44                              Address = n.Address,
 45                              Contect = n.Contect,
 46                              PhoneNumber = n.PhoneNumber,
 47                              RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
 48                            }).ToList();
 49         var pagelist = new { total = total, rows = pagerows };
 50         return Json(pagelist);
 51       }
 52       catch(Exception e) {
 53         throw e;
 54         }
 55 
 56     }
 57     //編輯 
 58     [HttpPost]
 59     [ValidateAntiForgeryToken]
 60     public async Task<JsonResult> Edit(Company company)
 61     {
 62       if (ModelState.IsValid)
 63       {
 64         try
 65         {
 66           this.companyService.Update(company);
 67 
 68           var result = await this.unitOfWork.SaveChangesAsync();
 69           return Json(new { success = true, result = result });
 70         }
 71          catch (Exception e)
 72         {
 73           return Json(new { success = false, err = e.GetBaseException().Message });
 74         }
 75       }
 76       else
 77       {
 78         var modelStateErrors = string.Join(",", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
 79         return Json(new { success = false, err = modelStateErrors });
 80         //DisplayErrorMessage(modelStateErrors);
 81       }
 82       //return View(work);
 83     }
 84     //新建
 85     [HttpPost]
 86     [ValidateAntiForgeryToken]
 87    
 88     public async Task<JsonResult> Create([Bind("Name,Code,Address,Contect,PhoneNumber,RegisterDate")] Company company)
 89     {
 90       if (ModelState.IsValid)
 91       {
 92         try
 93         {
 94           this.companyService.Insert(company);
 95        await this.unitOfWork.SaveChangesAsync();
 96           return Json(new { success = true});
 97         }
 98         catch (Exception e)
 99         {
100           return Json(new { success = false, err = e.GetBaseException().Message });
101         }
102 
103         //DisplaySuccessMessage("Has update a Work record");
104         //return RedirectToAction("Index");
105       }
106       else
107        {
108         var modelStateErrors = string.Join(",", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
109         return Json(new { success = false, err = modelStateErrors });
110         //DisplayErrorMessage(modelStateErrors);
111       }
112       //return View(work);
113     }
114     //刪除當前記錄
115     //GET: Companies/Delete/:id
116     [HttpGet]
117     public async Task<JsonResult> Delete(int id)
118     {
119       try
120       {
121         await this.companyService.DeleteAsync(id);
122         await this.unitOfWork.SaveChangesAsync();
123         return Json(new { success = true });
124       }
125      
126       catch (Exception e)
127       {
128         return Json(new { success = false, err = e.GetBaseException().Message });
129       }
130     }
131     //刪除選中的記錄
132     [HttpPost]
133     public async Task<JsonResult> DeleteChecked(int[] id)
134     {
135       try
136       {
137         foreach (var key in id)
138         {
139           await this.companyService.DeleteAsync(key);
140         }
141         await this.unitOfWork.SaveChangesAsync();
142         return Json(new { success = true });
143       }
144       catch (Exception e)
145       {
146         return Json(new { success = false, err = e.GetBaseException().Message });
147       }
148     }
149     //保存datagrid編輯的數據
150     [HttpPost]
151     public async Task<JsonResult> AcceptChanges(Company[] companies)
152     {
153       if (ModelState.IsValid)
154       {
155         try
156         {
157           foreach (var item in companies)
158           {
159             this.companyService.ApplyChanges(item);
160           }
161           var result = await this.unitOfWork.SaveChangesAsync();
162           return Json(new { success = true, result });
163         }
164         catch (Exception e)
165         {
166           return Json(new { success = false, err = e.GetBaseException().Message });
167         }
16

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、display顯示與隱藏 (1)display的屬性值 none:隱藏元素 block:轉換為塊級元素、顯示元素 (2)隱藏 不添加隱藏盒子的屬性 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test</title> ...
  • 第一步:父層設置文本居中屬性。第二步:li設置內聯樣式。只需以上兩步就可以實現導航欄居中顯示了,但為了美觀好看,可以稍微加點料。以下供參考:list-style:none; 取消列表前面的列表樣式border-radius:25px; 設置圓角背景樣式text-decoration:none; 取消... ...
  • 前言 響應式原理作為 Vue 的核心,使用數據劫持實現數據驅動視圖。在面試中是經常考查的知識點,也是面試加分項。 本文將會循序漸進的解析響應式原理的工作流程,主要以下麵結構進行: 分析主要成員,瞭解它們有助於理解流程 將流程拆分,理解其中的作用 結合以上的點,理解整體流程 文章稍長,但部分是代碼,還 ...
  • ●存儲大小的不同: localStorage的大小一般為5M sessionStorage的大小一般為5M cookies的大小一般為4K ●有效期不同: 1.localStorage的有效期為永久有效,除非你進行手動刪除。 2.sessionStorage在當前會話下有效,關閉頁面或者瀏覽器時會被 ...
  • 我們使用原生JS+CSS3,來開發一個集趣味性與技術性於一體的H5小游戲。我們會就地講解項目中用到的css,js基礎知識點,先舉一些小的示例,來闡明知識點的用法,再說明如何在本項目中應用,應用在哪塊功能的實現上。比如標準文檔流,定位,浮動,盒子模型,CSS3彈性盒子,CSS3高級選擇器,背景圖片,j... ...
  • 1、定位的疊放次序(只有定位的盒子才擁有這個屬性) (1)在使用定位佈局的時候,可能會出現盒子重疊的情況,此時,可以使用z-index來控制盒子的前後次序。該屬性的值可以是正整數、負整數或0,預設是auto,數值越大,盒子越靠上 <!DOCTYPE html> <html> <head> <meta ...
  • 面向對象三要素:封裝、繼承、多態。 封裝和繼承,這兩個比較好理解,但要理解多態的話,可就稍微有點難度了。今天,我們就來講講多態的理解。 我們應該經常會看到面試題目:請談談對多態的理解。 其實呢,多態非常簡單,就一句話:調用同一種方法產生了不同的結果。 具體實現方式有三種。 一、重載 重載很簡單。 p ...
  • 為什麼要搭建港股交易平臺?那就要問股民們為什麼會選擇港股進行投資了?一般的股民可能有人會說:還能為啥啊?賺錢唄!!!但是選擇投資港股,卻可能不是簡單的賺錢這麼簡單。可能很多人並不瞭解香港市場,畢竟這是一個境外市場,運作機制和操作風格都和我們熟悉的A股有很大的不同。 與A股相比,港股的優勢是比較明顯的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...