我們為什麼要使用AOP?

来源:http://www.cnblogs.com/xrq730/archive/2017/06/19/7003082.html
-Advertisement-
Play Games

原文地址http://www.cnblogs.com/xrq730/p/7003082.html,轉載請註明出處,謝謝 前言 一年半前寫了一篇文章Spring3:AOP,是當時學習如何使用Spring AOP的時候寫的,比較基礎。這篇文章最後的推薦以及回覆認為我寫的對大家有幫助的評論有很多,但是現在 ...


原文地址http://www.cnblogs.com/xrq730/p/7003082.html,轉載請註明出處,謝謝

 

前言

一年半前寫了一篇文章Spring3:AOP,是當時學習如何使用Spring AOP的時候寫的,比較基礎。這篇文章最後的推薦以及回覆認為我寫的對大家有幫助的評論有很多,但是現在從我個人的角度來看,這篇文章寫得並不好,甚至可以說是沒有太多實質性的內容,因此這些推薦和評論讓我覺得受之有愧。

基於以上原因,更新一篇文章,從最基礎的原始代碼-->使用設計模式(裝飾器模式與代理)-->使用AOP三個層次來講解一下為什麼我們要使用AOP,希望這篇文章可以對網友朋友們有益。

 

原始代碼的寫法

既然要通過代碼來演示,那必須要有例子,這裡我的例子為:

有一個介面Dao有insert、delete、update三個方法,在insert與update被調用的前後,列印調用前的毫秒數與調用後的毫秒數

首先定義一個Dao介面:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public interface Dao {
 5 
 6     public void insert();
 7     
 8     public void delete();
 9     
10     public void update();
11     
12 }

然後定義一個實現類DaoImpl:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class DaoImpl implements Dao {
 5 
 6     @Override
 7     public void insert() {
 8         System.out.println("DaoImpl.insert()");
 9     }
10 
11     @Override
12     public void delete() {
13         System.out.println("DaoImpl.delete()");
14     }
15 
16     @Override
17     public void update() {
18         System.out.println("DaoImpl.update()");
19     }
20     
21 }

最原始的寫法,我要在調用insert()與update()方法前後分別列印時間,就只能定義一個新的類包一層,在調用insert()方法與update()方法前後分別處理一下,新的類我命名為ServiceImpl,其實現為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class ServiceImpl {
 5 
 6     private Dao dao = new DaoImpl();
 7     
 8     public void insert() {
 9         System.out.println("insert()方法開始時間:" + System.currentTimeMillis());
10         dao.insert();
11         System.out.println("insert()方法結束時間:" + System.currentTimeMillis());
12     }
13     
14     public void delete() {
15         dao.delete();
16     }
17     
18     public void update() {
19         System.out.println("update()方法開始時間:" + System.currentTimeMillis());
20         dao.update();
21         System.out.println("update()方法結束時間:" + System.currentTimeMillis());
22     }
23     
24 }

這是最原始的寫法,這種寫法的缺點也是一目瞭然:

  1. 方法調用前後輸出時間的邏輯無法復用,如果有別的地方要增加這段邏輯就得再寫一遍
  2. 如果Dao有其它實現類,那麼必須新增一個類去包裝該實現類,這將導致類數量不斷膨脹

 

使用裝飾器模式

接著我們使用上設計模式,先用裝飾器模式,看看能解決多少問題。裝飾器模式的核心就是實現Dao介面並持有Dao介面的引用,我將新增的類命名為LogDao,其實現為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class LogDao implements Dao {
 5 
 6     private Dao dao;
 7     
 8     public LogDao(Dao dao) {
 9         this.dao = dao;
10     }
11 
12     @Override
13     public void insert() {
14         System.out.println("insert()方法開始時間:" + System.currentTimeMillis());
15         dao.insert();
16         System.out.println("insert()方法結束時間:" + System.currentTimeMillis());
17     }
18 
19     @Override
20     public void delete() {
21         dao.delete();
22     }
23 
24     @Override
25     public void update() {
26         System.out.println("update()方法開始時間:" + System.currentTimeMillis());
27         dao.update();
28         System.out.println("update()方法結束時間:" + System.currentTimeMillis());
29     }
30 
31 }

在使用的時候,可以使用"Dao dao = new LogDao(new DaoImpl())"的方式,這種方式的優點為:

  1. 透明,對調用方來說,它只知道Dao,而不知道加上了日誌功能
  2. 類不會無限膨脹,如果Dao的其它實現類需要輸出日誌,只需要向LogDao的構造函數中傳入不同的Dao實現類即可

不過這種方式同樣有明顯的缺點,缺點為:

  1. 輸出日誌的邏輯還是無法復用
  2. 輸出日誌的邏輯與代碼有耦合,如果我要對delete()方法前後同樣輸出時間,需要修改LogDao

但是,這種做法相比最原始的代碼寫法,已經有了很大的改進。

 

使用代理模式

接著我們使用代理模式嘗試去實現最原始的功能,使用代理模式,那麼我們就要定義一個InvocationHandler,我將它命名為LogInvocationHandler,其實現為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class LogInvocationHandler implements InvocationHandler {
 5 
 6     private Object obj;
 7     
 8     public LogInvocationHandler(Object obj) {
 9         this.obj = obj;
10     }
11     
12     @Override
13     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14         String methodName = method.getName();
15         if ("insert".equals(methodName) || "update".equals(methodName)) {
16             System.out.println(methodName + "()方法開始時間:" + System.currentTimeMillis());
17             Object result = method.invoke(obj, args);
18             System.out.println(methodName + "()方法結束時間:" + System.currentTimeMillis());
19             
20             return result;
21         }
22         
23         return method.invoke(obj, args);
24     }
25     
26 }

其調用方式很簡單,我寫一個main函數:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public static void main(String[] args) {
 5     Dao dao = new DaoImpl();
 6         
 7     Dao proxyDao = (Dao)Proxy.newProxyInstance(LogInvocationHandler.class.getClassLoader(), new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));
 8         
 9     proxyDao.insert();
10     System.out.println("----------分割線----------");
11     proxyDao.delete();
12     System.out.println("----------分割線----------");
13     proxyDao.update();
14 }

結果就不演示了,這種方式的優點為:

  1. 輸出日誌的邏輯被覆用起來,如果要針對其他介面用上輸出日誌的邏輯,只要在newProxyInstance的時候的第二個參數增加Class<?>數組中的內容即可

這種方式的缺點為:

  1. JDK提供的動態代理只能針對介面做代理,不能針對類做代理
  2. 代碼依然有耦合,如果要對delete方法調用前後列印時間,得在LogInvocationHandler中增加delete方法的判斷

 

使用CGLIB

接著看一下使用CGLIB的方式,使用CGLIB只需要實現MethodInterceptor介面即可:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class DaoProxy implements MethodInterceptor {
 5 
 6     @Override
 7     public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
 8         String methodName = method.getName();
 9         
10         if ("insert".equals(methodName) || "update".equals(methodName)) {
11             System.out.println(methodName + "()方法開始時間:" + System.currentTimeMillis());
12             proxy.invokeSuper(object, objects);
13             System.out.println(methodName + "()方法結束時間:" + System.currentTimeMillis());
14             
15             return object;
16         }
17         
18         proxy.invokeSuper(object, objects);
19         return object;
20     }
21 
22 }

代碼調用方式為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public static void main(String[] args) {
 5     DaoProxy daoProxy = new DaoProxy();
 6     
 7     Enhancer enhancer = new Enhancer();
 8     enhancer.setSuperclass(DaoImpl.class);
 9     enhancer.setCallback(daoProxy);
10         
11     Dao dao = (DaoImpl)enhancer.create();
12     dao.insert();
13     System.out.println("----------分割線----------");
14     dao.delete();
15     System.out.println("----------分割線----------");
16     dao.update();
17 }

使用CGLIB解決了JDK的Proxy無法針對類做代理的問題,但是這裡要專門說明一個問題:使用裝飾器模式可以說是對使用原生代碼的一種改進,使用Java代理可以說是對於使用裝飾器模式的一種改進,但是使用CGLIB並不是對於使用Java代理的一種改進

前面的可以說改進是因為使用裝飾器模式比使用原生代碼更好,使用Java代理又比使用裝飾器模式更好,但是Java代理與CGLIb的對比並不能說改進,因為使用CGLIB並不一定比使用Java代理更好,這兩種各有優缺點,像Spring框架就同時支持Java Proxy與CGLIB兩種方式。

從目前看來代碼又更好了一些,但是我認為還有兩個缺點:

  1. 無論使用Java代理還是使用CGLIB,編寫這部分代碼都稍顯麻煩
  2. 代碼之間的耦合還是沒有解決,像要針對delete()方法加上這部分邏輯就必須修改代碼

 

使用AOP

最後來看一下使用AOP的方式,首先定義一個時間處理類,我將它命名為TimeHandler:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class TimeHandler {
 5     
 6     public void printTime(ProceedingJoinPoint pjp) {
 7         Signature signature = pjp.getSignature();
 8         if (signature instanceof MethodSignature) {
 9             MethodSignature methodSignature = (MethodSignature)signature;
10             Method method = methodSignature.getMethod();
11             System.out.println(method.getName() + "()方法開始時間:" + System.currentTimeMillis());
12             
13             try {
14                 pjp.proceed();
15                 System.out.println(method.getName() + "()方法結束時間:" + System.currentTimeMillis());
16             } catch (Throwable e) {
17                 
18             }
19         }
20     }
21     
22 }

到第8行的代碼與第12行的代碼分別列印方法開始執行時間與方法結束執行時間。我這裡寫得稍微複雜點,使用了<aop:around>的寫法,其實也可以拆分為<aop:before>與<aop:after>兩種,這個看個人喜好。

這裡多說一句,切麵方法printTime本身可以不用定義任何的參數,但是有些場景下需要獲取調用方法的類、方法簽名等信息,此時可以在printTime方法中定義JointPoint,Spring會自動將參數註入,可以通過JoinPoint獲取調用方法的類、方法簽名等信息。由於這裡我用的<aop:around>,要保證方法的調用,這樣才能在方法調用前後輸出時間,因此不能直接使用JoinPoint,因為JoinPoint沒法保證方法調用。此時可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保證方法調用,但是要註意一點,ProceedingJoinPoint只能和<aop:around>搭配,換句話說,如果aop.xml中配置的是<aop:before>,然後printTime的方法參數又是ProceedingJoinPoint的話,Spring容器啟動將報錯。

接著看一下aop.xml的配置:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/aop
 9         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
10 
11     <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />
12     <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />
13 
14     <aop:config>
15         <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />
16         <aop:aspect id="time" ref="timeHandler">
17             <aop:before method="printTime" pointcut-ref="addAllMethod" />
18             <aop:after method="printTime" pointcut-ref="addAllMethod" />
19         </aop:aspect>
20     </aop:config>
21     
22 </beans>

我不大會寫expression,也懶得去百度了,因此這裡就攔截Dao下的所有方法了。測試代碼很簡單:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class AopTest {
 5 
 6     @Test
 7     @SuppressWarnings("resource")
 8     public void testAop() {
 9         ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");
10         
11         Dao dao = (Dao)ac.getBean("daoImpl");
12         dao.insert();
13         System.out.println("----------分割線----------");
14         dao.delete();
15         System.out.println("----------分割線----------");
16         dao.update();
17     }
18     
19 }

結果就不演示了。到此我總結一下使用AOP的幾個優點:

  1. 切麵的內容可以復用,比如TimeHandler的printTime方法,任何地方需要列印方法執行前的時間與方法執行後的時間,都可以使用TimeHandler的printTime方法
  2. 避免使用Proxy、CGLIB生成代理,這方面的工作全部框架去實現,開發者可以專註於切麵內容本身
  3. 代碼與代碼之間沒有耦合,如果攔截的方法有變化修改配置文件即可

下麵用一張圖來表示一下AOP的作用:

我們傳統的編程方式是垂直化的編程,即A-->B-->C-->D這麼下去,一個邏輯完畢之後執行另外一段邏輯。但是AOP提供了另外一種思路,它的作用是在業務邏輯不知情(即業務邏輯不需要做任何的改動)的情況下對業務代碼的功能進行增強,這種編程思想的使用場景有很多,例如事務提交、方法執行之前的許可權檢測、日誌列印、方法調用事件等等。

 

AOP使用場景舉例

上面的例子純粹為了演示使用,為了讓大家更加理解AOP的作用,這裡以實際場景作為例子。

第一個例子,我們知道MyBatis的事務預設是不會自動提交的,因此在編程的時候我們必須在增刪改完畢之後調用SqlSession的commit()方法進行事務提交,這非常麻煩,下麵利用AOP簡單寫一段代碼幫助我們自動提交事務(這段代碼我個人測試過可用):

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class TransactionHandler {
 5 
 6     public void commit(JoinPoint jp) {
 7         Object obj = jp.getTarget();
 8         if (obj instanceof MailDao) {
 9             Signature signature = jp.getSignature();
10             if (signature instanceof MethodSignature) {
11                 SqlSession sqlSession = SqlSessionThrealLocalUtil.getSqlSession();                
12                 
13                 MethodSignature methodSignature = (MethodSignature)signature;
14                 Method method = methodSignature.getMethod();
15                  
16                 String methodName = method.getName();
17                 if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) {
18                     sqlSession.commit();
19                 }
20                 
21                 sqlSession.close();
22             }
23         }
24     }
25     
26 }

這種場景下我們要使用的aop標簽為<aop:after>,即切在方法調用之後。

這裡我做了一個SqlSessionThreadLocalUtil,每次打開會話的時候,都通過SqlSessionThreadLocalUtil把當前會話SqlSession放到ThreadLocal中,看到通過TransactionHandler,可以實現兩個功能:

  1. insert、update、delete操作事務自動提交
  2. 對SqlSession進行close(),這樣就不需要在業務代碼裡面關閉會話了,因為有些時候我們寫業務代碼的時候會忘記關閉SqlSession,這樣可能會造成記憶體句柄的膨脹,因此這部分切麵也一併做了

整個過程,業務代碼是不知道的,而TransactionHandler的內容可以充分再多處場景下進行復用。

第二個例子是許可權控制的例子,不管是從安全形度考慮還是從業務角度考慮,我們在開發一個Web系統的時候不可能所有請求都對所有用戶開放,因此這裡就需要做一層許可權控制了,大家看AOP作用的時候想必也肯定會看到AOP可以做許可權控制,這裡我就演示一下如何使用AOP做許可權控制。我們知道原生的Spring MVC,Java類是實現Controller介面的,基於此,利用AOP做許可權控制的大致代碼如下(這段代碼純粹就是一段示例,我構建的Maven工程是一個普通的Java工程,因此沒有驗證過):

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7003082.html
 3  */
 4 public class PermissionHandler {
 5 
 6     public void hasPermission(JoinPoint jp) throws Exception {
 7         Object obj = jp.getTarget();
 8         
 9         if (obj instanceof Controller) {
10             Signature signature = jp.getSignature();
11             MethodSignature methodSignature = (MethodSignature)signature;
12             
13             // 獲取方法簽名
14             Method method = methodSignature.getMethod();
15             // 獲取方法參數
16             Object[] args = jp.getArgs();
17             
18             // Controller中唯一一個方法的方法簽名ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
19             // 這裡對這個方法做一層判斷
20             if ("handleRequest".equals(method.getName()) && args.length == 2) {
21                 Object firstArg = args[0];
22                 if (obj instanceof HttpServletRequest) {
23                     HttpServletRequest request = (HttpServletRequest)firstArg;
24                     // 獲取用戶id
25                     long userId = Long.parseLong(request.getParameter("userId"));
26                     // 獲取當前請求路徑
27                     String requestUri = request.getRequestURI();
28                     
29                     if(!PermissionUtil.hasPermission(userId, requestUri)) {
30                         throw new Exception("沒有許可權");
31                     }
32                 }
33             }
34         }
35         
36     }
37     
38 }

毫無疑問這種場景下我們要使用的aop標簽為<aop:before>。這裡我寫得很簡單,獲取當前用戶id與請求路徑,根據這兩者,判斷該用戶是否有許可權訪問該請求,大家明白意思即可。

 

後記

文章演示了從原生代碼到使用AOP的過程,一點一點地介紹了每次演化的優缺點,最後以實際例子分析了AOP可以做什麼事情。

之前的那篇AOP入門的文章Spring3:AOP再結合上這篇文章,希望可以真正對網友朋友們有益。


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

-Advertisement-
Play Games
更多相關文章
  • SqlSugar 4.0 ORM框架的優勢 為了未來能夠更好的支持多庫分散式的存儲,並行計算等功能,將SqlSugar3.x全部重寫,現有的架構可以輕鬆擴展多庫。 源碼下載: https://github.com/sunkaixuan/SqlSugar 1.性能 性能最好的ORM之一,具有超越Dap ...
  • 上一篇中講到XML基本的結構,還有增刪改查的方法,這一篇中我們就來利用XML來完成一個簡單的訂單系統,主要是實現一個簡單學生名單的增刪改查,如果想要應用到實際的環境中建議考慮數據量的問題,如果數據量大使用XML的話會比較耗時,使用SQL的性能會好一些 這裡使用WinForm窗體程式,大致界面如下: ...
  • 在講了一系列的基礎文檔之後,現在開始講一些實例。對於一些數據不是很大的程式,或者只是一些配置文件,需要本地存儲的,完全可以使用XML代替資料庫,因為只是去操作單個文件會比操作資料庫要簡單很多,在程式中訪問和操作XML一般使用DOM(文檔對象模型)和流模式。DOM運行編輯和更新XML文檔,可以隨機訪問 ...
  • 之前兩篇文檔講述了C#中的面向對象的概念,其實這個概念對於很多種語言都是通用的,只不過每種語言具體實現的過程方法不一樣,比如Java、C++、python等,這些都是很流行的面向對象的語言在編程語言排行榜中都是屬於前幾名的。面向對象中比較難以理解的就是多態的概念,多態我將留到後面的章節去講,現在繼續 ...
  • 1、安裝ElasticSearch https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html 這個頁面有詳細安裝步驟 2、安裝Head插件 head插件可以管理elasticsearch集群,管理 ...
  • 1.新建Netcore Web項目 2.創建簡易通訊協議 SenderID發送者ID ReceiverID 接受者ID MessageType 消息類型 Text Voice 等等 Content 消息內容 3.添加中間件ChatWebSocketMiddleware 4.在Startup.cs中使 ...
  • 現在市面上的編程語言以面向對象為主流。面向對象先要從一些最基本的做起。比如我24歲就結婚了,不然怎麼面向對象編程。然後剛結婚就生娃了,不然對象跑了咋辦?new一個?創建銷毀開銷很大的,還是生個娃持續持有對象的引用的好。 為啥有些人開口說話能說很久,有些人說話有一搭沒一搭的?據我觀察發現,動手幹活差不 ...
  • 首先要導入 包. 下載解壓後, 目錄下有三個包,使用 的話,只需要導入 ,`mchange commons java 0.2.11.jar`. 要連接 資料庫,需要導入 . 為了方便的操作資料庫鏈接進行查詢,需要導入 ,`commons collections 3.2.2.jar commons l ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...