Asp.Net WebApi核心對象解析(下篇)

来源:http://www.cnblogs.com/pengze0902/archive/2017/01/03/6230105.html
-Advertisement-
Play Games

在接著寫Asp.Net WebApi核心對象解析(下篇)之前,還是一如既往的扯扯淡,元旦剛過,整個人還是處於暈的狀態,一大早就來處理系統BUG,簡直是坑爹(好在沒讓我元旦趕過來該BUG),隊友挖的坑,還讓我含著淚去填。改BUG前看類看隊友寫的代碼,這裡就不評價了,反正是邊改邊罵,我的嘴巴就沒停過,作 ...


   在接著寫Asp.Net WebApi核心對象解析(下篇)之前,還是一如既往的扯扯淡,元旦剛過,整個人還是處於暈的狀態,一大早就來處理系統BUG,簡直是坑爹(好在沒讓我元旦趕過來該BUG),隊友挖的坑,還讓我含著淚去填。改BUG前看隊友寫的代碼,這裡就不評價了,反正是邊改邊罵,我的嘴巴就沒停過,作為開發者,我那時的心情,就不再描述了,反正是找不到一個好詞形容。

   新年展望,我感覺我是沒啥好展望的,反正去年的展望是一個都沒實現,事情該怎麼做還是怎麼做的,估計大多數人跟我差不多,任何事不能強求,事前努力去辦,事後得不到也就看淡一點,太苛求,遲早身心俱疲。

   扯淡完畢,接著聊正事,上一篇寫的是Asp.Net WebApi核心對象解析(上篇),本文是下篇,不管寫的怎麼樣,還望大家多多指正。

一.WebApi處理架構:

    我們在學習Asp.Net WebApi時,應該對Asp.Net WebApi的內部運行機制有一個大致的瞭解,很多人說瞭解這些基本原理的意義不大,實際開發中應用不到而且還浪費時間,這樣說有一定的道理,但是如果我們的眼光放的長遠一些,就不會這樣想聊,我們瞭解基本原理後,可以在一定的程度上幫助我們處理一些程式底層的bug,而且還有可以讓我們從中學會思考,去深入的理解設計者的意圖,有利於我們更加熟練的運用。

    在談WebApi處理架構之前,我們還是來看一下微軟為WebApi提供的海報,這裡就不拿圖了,需要看的可以點擊下載:下載地址

    Asp.Net Web Api處理架構可以分為三層,分別是托管層、消息處理程式管道、控制器處理。

       托管層:位於WebApi和底層HTTP棧之間,是最底層負責WebApi托管。

       消息處理程式管道層:用與實現消息的橫切關註點,例如日誌和緩存。

       控制器處理層:控制器和操作是在這一層進行調用,參數再次綁定和驗證,HTTP響應消息也在這裡創建。

    對於托管層測說明,會在下麵進行講解。消息處理程式是對一個操作的抽象,它接受HTTP請求消息並返回HTTP響應消息。連接消息處理程式管道和控制器處理層的橋梁是控制器分發程式。控制器分發還是一個消息處理程式,主要是選擇、創建和調用正確的控制器來處理請求。

二.WebApi托管方式解析:

    在Asp.Net Web Api的托管方式有三種,接下來我們來大致瞭解一下這三種托管方式。

       (1).在任何Windows進程中自托管。

       (2).Web托管,即在IIS之上使用ASP.NET管道進行托管。(如果需要瞭解IIS和ASPI.NET管道的知識,可以自己搜索查看,筆者建議做web開發的人員瞭解一下其運行機制,有利於我們對asp.net web程式有一個深入的瞭解。)

       (3).OWIN托管。(在一個owin相容的伺服器上建立一個webapi層)

     在使用web托管時,所使用的是ASP.NET的管道和路由功能,將HTTP請求轉發到一個新的ASP.NET處理程式,HttpControllerHandler中。這個程式接收到HtppRequest實例轉換成HttpRequestMesssage實例,然後推送到WebApi管道,從而在傳統的asp.net管道和新的asp.net webapi架構間建立起鏈接。

     這裡我們具體瞭解一下HttpControllerHandler這個類:

     HttpControllerHandler類在 System.Web.Http.WebHost命名空間下,根據命名空間的名稱,我們就可以清晰的瞭解到該命名空間主要用於創建web托管的。

public class HttpControllerHandler : IHttpAsyncHandler, IHttpHandler

 該類繼承自IHttpAsyncHandler, IHttpHandler介面,我們由底層代碼可知,該類實際具體繼承自HttpTaskAsyncHandler類,用與Http任務非同步處理程式。接下來我們具體看一下該類的一些方法:

  1.ProcessRequestAsync方法:提供處理非同步任務的代碼。

    /// <summary>
    /// 提供處理非同步任務的代碼
    /// </summary>
    /// <returns>
    /// 非同步任務。
    /// </returns>
    /// <param name="context">HTTP 上下文。</param>
    public override Task ProcessRequestAsync(HttpContext context)
    {
      return this.ProcessRequestAsyncCore((HttpContextBase) new HttpContextWrapper(context));
    }

    internal async Task ProcessRequestAsyncCore(HttpContextBase contextBase)
    {
      HttpRequestMessage request = HttpContextBaseExtensions.GetHttpRequestMessage(contextBase) ?? HttpControllerHandler.ConvertRequest(contextBase);
      System.Net.Http.HttpRequestMessageExtensions.SetRouteData(request, this._routeData);
      CancellationToken cancellationToken = HttpResponseBaseExtensions.GetClientDisconnectedTokenWhenFixed(contextBase.Response);
      HttpResponseMessage response = (HttpResponseMessage) null;
      try
      {
        response = await this._server.SendAsync(request, cancellationToken);
        await HttpControllerHandler.CopyResponseAsync(contextBase, request, response, HttpControllerHandler._exceptionLogger.Value, HttpControllerHandler._exceptionHandler.Value, cancellationToken);
      }
      catch (OperationCanceledException ex)
      {
        contextBase.Request.Abort();
      }
      finally
      {
        System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources(request);
        request.Dispose();
        if (response != null)
          response.Dispose();
      }
    }

   該方法是一個非同步方法,並且接收的參數是HttpContext,表示http上下文內容,調用GetHttpRequestMessage()獲取HttpRequestMessage對象實例,調用SetRouteData()方法設置路由信息,調用GetClientDisconnectedTokenWhenFixed()方法獲取客戶端斷開令牌時修複,並返回取消令牌,該方法生成http請求後,對消息進行非同步發送處理操作。

   2.GetStreamContent方法:獲取請求獲取流內容。

 private static HttpContent GetStreamContent(HttpRequestBase requestBase, bool bufferInput)
    {
      if (bufferInput)
        return (HttpContent) new HttpControllerHandler.LazyStreamContent((Func<Stream>) (() =>
        {
          if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.None)
            return (Stream) new SeekableBufferedRequestStream(requestBase);
          if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Classic)
          {
            requestBase.InputStream.Position = 0L;
            return requestBase.InputStream;
          }
          if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Buffered)
          {
            if (requestBase.GetBufferedInputStream().Position <= 0L)
              return (Stream) new SeekableBufferedRequestStream(requestBase);
            requestBase.InputStream.Position = 0L;
            return requestBase.InputStream;
          }
          throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, SRResources.RequestBodyAlreadyReadInMode, new object[1]
          {
            (object) ReadEntityBodyMode.Bufferless
          }));
        }));
      return (HttpContent) new HttpControllerHandler.LazyStreamContent((Func<Stream>) (() =>
      {
        if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.None)
          return requestBase.GetBufferlessInputStream();
        if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Classic)
          throw new InvalidOperationException(SRResources.RequestStreamCannotBeReadBufferless);
        if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Bufferless)
        {
          Stream bufferlessInputStream = requestBase.GetBufferlessInputStream();
          if (bufferlessInputStream.Position > 0L)
            throw new InvalidOperationException(SRResources.RequestBodyAlreadyRead);
          return bufferlessInputStream;
        }
        throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, SRResources.RequestBodyAlreadyReadInMode, new object[1]
        {
          (object) ReadEntityBodyMode.Buffered
        }));
      }));
    }

   該方法用與獲取HTTP請求的流內容,根據參數HttpRequestBase可知,該方法接受到HTTP請求後,對消息進行處理,bufferInput參數判斷傳入的是否為流對象,傳入的流對象,進入LazyStreamContent類進行處理,LazyStreamContent類的構造函數接受一個含有返回值的委托。

public LazyStreamContent(Func<Stream> getStream)
      {
        this._getStream = getStream;
      }

   GetStreamContent方法的相關操作主要是對HTTP請求內容的解析操作。

三.WebApi核心對象HttpRequestMessage和HttpResponseMessage:

   1.HttpRequestMessageExtensions:HTTP消息請求實例的擴展類。

      (1).GetRequestContext方法:獲取HTTP請求消息內容:

 public static HttpRequestContext GetRequestContext(this HttpRequestMessage request)
    {
      if (request == null)
        throw Error.ArgumentNull("request");
      return HttpRequestMessageExtensions.GetProperty<HttpRequestContext>(request, HttpPropertyKeys.RequestContextKey);
    }

 根據傳入的HTTP請求,調用GetProperty()方法獲取屬性,我們具體看一下GetProperty()方法:

 private static T GetProperty<T>(this HttpRequestMessage request, string key)
    {
      T obj;
      DictionaryExtensions.TryGetValue<T>(request.Properties, key, out obj);
      return obj;
    }

該方法獲取請求對象,並根據KEY值調用TryGetValue()方法獲取屬性。

   (2).CreateResponse():創建請求信息的響應。

    /// <summary>
    /// 創建與關聯的 HttpRequestMessage連接的HttpResponseMessage
    /// </summary>
    /// <returns>
    /// 與關聯的 HttpRequestMessage連接的已初始化 HttpResponseMessage
    /// </returns>
    /// <param name="request">導致此響應消息的 HTTP 請求消息。</param>
<param name="statusCode">
HTTP 響應狀態代碼。</param>
<param name="value">
HTTP 響應消息的內容。</param>
<param name="configuration">
包含用於解析服務的依賴關係解析程式的 HTTP 配置。</param>
<typeparam name="T">
HTTP 響應消息的類型。</typeparam> public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration) { if (request == null) throw Error.ArgumentNull("request"); configuration = configuration ?? HttpRequestMessageExtensions.GetConfiguration(request); if (configuration == null) throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration); IContentNegotiator contentNegotiator = ServicesExtensions.GetContentNegotiator(configuration.Services); if (contentNegotiator == null) throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, (object) typeof (IContentNegotiator).FullName); IEnumerable<MediaTypeFormatter> formatters = (IEnumerable<MediaTypeFormatter>) configuration.Formatters; return NegotiatedContentResult<T>.Execute(statusCode, value, contentNegotiator, request, formatters); }

 該方法根據請求消息提供的相關信息,在處理完畢請求消息後,創建響應消息內容。

   2.HttpResponseMessageExtensions:HTTP應答消息實例的擴展類。

     TryGetContentValue():獲取內容的值。

    /// <summary>
    /// 嘗試檢索HttpResponseMessageExtensions 的內容的值。
    /// </summary>
    /// <returns>
    /// 內容值的檢索結果。
    /// </returns>
    /// <param name="response">操作的響應。</param>
         <param name="value">內容的值。</param>
         <typeparam name="T">要檢索的值的類型。</typeparam>
    public static bool TryGetContentValue<T>(this HttpResponseMessage response, out T value)
    {
      if (response == null)
        throw Error.ArgumentNull("response");
      ObjectContent objectContent = response.Content as ObjectContent;
      if (objectContent != null && objectContent.Value is T)
      {
        value = (T) objectContent.Value;
        return true;
      }
      value = default (T);
      return false;
    }

   根據傳入的響應消息對象獲取響應消息的內容。

四.WebApi核心對象HttpClient:

   上面介紹完伺服器端的接收和響應HTTP請求的操作方法,接下來介紹一個客戶端生成HTTP請求,用與請求和獲取伺服器返回的消息,在新版本的.NET中,提供類HTTPClient類用來在客戶端生成和獲取HTTP請求的類。

    1.屬性概要:

      BaseAddress:獲取或設置發送請求時所使用的互聯網資源的統一資源標識符(URI)的基地址。

      DefaultRequestHeaders:獲取應隨每個請求發送的頭。

      MaxResponseContentBufferSize:獲取或設置中的最大位元組數讀取響應內容時緩衝。

     Timeout:獲取或設置的毫秒數請求超時之前等待。

   2.方法概要:

     CancelPendingRequests:取消此實例上的所有未決請求。

     DeleteAsync(String):發送一個DELETE請求到指定的URI為非同步操作。

     GetAsync(String):發送GET請求到指定的URI為非同步操作。

     GetStreamAsync(String):發送GET請求到指定的URI並返迴響應主體作為一個非同步操作流。

     PostAsync(String, HttpContent):發送POST請求到指定的URI作為一個非同步操作。

     SendAsync(HttpRequestMessage):發送一個HTTP請求作為一個非同步操作。

   3.方法和屬性解析:

     (1).BaseAddress:獲取或設置發送請求時所使用的互聯網資源的統一資源標識符(URI)的基地址。

    /// <summary>
    /// 獲取或設置發送請求時使用的 Internet 資源的統一資源標識符 (URI) 的基址。
    /// </summary>
    /// <returns>
    /// 返回 <see cref="T:System.Uri"/>。發送請求時使用的 Internet 資源的統一資源標識符 (URI) 的基址。
    /// </returns>
    [__DynamicallyInvokable]
    public Uri BaseAddress
    {
      [__DynamicallyInvokable] get
      {
        return this.baseAddress;
      }
      [__DynamicallyInvokable] set
      {
        HttpClient.CheckBaseAddress(value, "value");
        this.CheckDisposedOrStarted();
        if (Logging.On)
          Logging.PrintInfo(Logging.Http, (object) this, "BaseAddress: '" + (object) this.baseAddress + "'");
        this.baseAddress = value;
      }
    }

     (2).GetContentAsync:根據指定的uri非同步的獲取內容。

private Task<T> GetContentAsync<T>(Uri requestUri, HttpCompletionOption completionOption, T defaultValue, Func<HttpContent, Task<T>> readAs)
    {
      TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
      HttpUtilities.ContinueWithStandard<HttpResponseMessage>(this.GetAsync(requestUri, completionOption), (Action<Task<HttpResponseMessage>>) (requestTask =>
      {
        if (HttpClient.HandleRequestFaultsAndCancelation<T>(requestTask, tcs))
          return;
        HttpResponseMessage result = requestTask.Result;
        if (result.Content == null)
        {
          tcs.TrySetResult(defaultValue);
        }
        else
        {
          try
          {
            HttpUtilities.ContinueWithStandard<T>(readAs(result.Content), (Action<Task<T>>) (contentTask =>
            {
              if (HttpUtilities.HandleFaultsAndCancelation<T>((Task) contentTask, tcs))
                return;
              tcs.TrySetResult(contentTask.Result);
            }));
          }
          catch (Exception ex)
          {
            tcs.TrySetException(ex);
          }
        }
      }));
      return tcs.Task;
    }

  該方法為非同步的方法,用與生成get、post請求後,獲取對應的內容。TrySetResult()方法將底層System.Threading.Tasks.Task`1轉換為RanToCompletion狀態。

     (3).SendAsync(): 以非同步操作發送 HTTP 請求。

    /// <summary>
    /// 以非同步操作發送 HTTP 請求。
    /// </summary>
    /// <returns>
    /// 返回 <see cref="T:System.Threading.Tasks.Task`1"/>。表示非同步操作的任務對象。
    /// </returns>
    /// <param name="request">要發送的 HTTP 請求消息。</param>
<param name="completionOption">
操作應完成時(在響應可利用或在讀取整個響應內容之後)。</param>
<param name="cancellationToken">
取消操作的取消標記。</param>
<exception cref="T:System.ArgumentNullException">
<paramref name="request"/>
為 null。</exception>
<exception cref="T:System.InvalidOperationException">
請求消息已由 <see cref="T:System.Net.Http.HttpClient"/> 實例發送。</exception> [__DynamicallyInvokable] public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) { if (request == null) throw new ArgumentNullException("request"); this.CheckDisposed(); HttpClient.CheckRequestMessage(request); this.SetOperationStarted(); this.PrepareRequestMessage(request); CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.pendingRequestsCts.Token); this.SetTimeout(linkedCts); TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>(); HttpUtilities.ContinueWithStandard<HttpResponseMessage>(base.SendAsync(request, linkedCts.Token), (Action<Task<HttpResponseMessage>>) (task => { try { this.DisposeRequestContent(request); if (task.IsFaulted) this.SetTaskFaulted(request, linkedCts, tcs, task.Exception.GetBaseException()); else if (task.IsCanceled) { this.SetTaskCanceled(request, linkedCts, tcs); } else { HttpResponseMessage result = task.Result; if (result == null) this.SetTaskFaulted(request, linkedCts, tcs, (Exception) new InvalidOperationException(SR.net_http_handler_noresponse)); else if (result.Content == null || completionOption == HttpCompletionOption.ResponseHeadersRead) this.SetTaskCompleted(request, linkedCts, tcs, result); else this.StartContentBuffering(request, linkedCts, tcs, result); } } catch (Exception ex) { if (Logging.On) Logging.Exception(Logging.Http, (object) this, "SendAsync", ex); tcs.TrySetException(ex); } })); return tcs.Task; }

   該方法是以非同步發的方法將HTTP請求發送出去,該方法的三個參數中,HttpRequestMessage表示http請求對象,HttpCompletionOption表示操作完成項,CancellationToken表示取消令牌。在發送HTTP請求之前,調用CheckRequestMessage方法對消息進行檢查。在使用非同步方法時,需要考慮操作的取消等外部因素對方法的影響。

   介紹完畢HttpClient對象,對於HttpClient的實際操作就不做介紹,HttpClient對象的使用非常的簡單,但是該類的底層實現還是比較的複雜。   

五.總結:

   本文分為上下兩篇,簡單的介紹類一下Asp.Net WebApi的一些核心對象,並簡單介紹了Asp.Net WebApi路由機制,處理架構,托管方式等等,如有不足和錯誤之處還望多多指正。(對我來說總算是寫完了,寫了上篇就得寫下篇,實在痛苦)


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

-Advertisement-
Play Games
更多相關文章
  • not on top of a bond Environment Red Hat Enterprise Linux 7 NetworkManager Issue Need an 802.1q VLAN in RHEL 7 when using NetworkManager. Resolution R ...
  • 我新裝的centos7主機無法使用裡面自帶的網卡,查詢後發現網卡型號為BCM4312。我在看資料安裝的過程中遇到了些問題,糾結了好久,現在分享下要註意的點,為後來的遇到同樣問題的人提供點幫助。現在開始說正事: 若要安裝以 Broadcom BCM4311、BCM4312、BCM4313、BCM432 ...
  • environment Red Hat Enterprise Linux 5.4 or later Red Hat Enterprise Linux 6.0 or later KVM virtual machines question How do I configure a bridged net ...
  • 修改環境變數PATH 最近為root添加一個環境變數發現sudo su進去沒有變化所以總結了一下所有設置環境變數的方法: 查看PATH: 1. 直接在命令行修改,就可以使用,但是只有在當前的視窗和用戶有用, 關閉以後就失效了,所以如果是臨時使用可以這樣設置 2. 修改家目錄下.bashrc文件,只對 ...
  • linux用戶分類 超級用戶:UID=0,root 普通用戶:UID 500起,由超級用戶或具有超級用戶許可權的用戶創建的用戶。 虛擬用戶:UID 1—499,為了滿足文件或服務啟動的需要而存在,一般都不能登錄,只是傀儡用戶。 用戶關聯的四個文件: /etc/passwd:用戶的賬號文件 /etc/s... ...
  • 準備工作: 實現lnmp環境 給php添加模塊,so庫 下載擴展包:memcache-2.2.5.tgz wget http://pecl.php.net/get/memcache-2.2.5.tgz ...
  • 最近在寫一個批量巡檢工具,利用ansible將腳本推到各個機器上執行,然後將執行的結果以json格式返回來。 如下所示: # ansible node2 -m script -a /root/python/health_check.py 然後將結果重定向到一個文本文件中,再通過另外一個腳本,對該文本 ...
  • .Net程式員們每天都在和Object在打交道 如果你問一個.Net程式員什麼是Object,他可能會信誓旦旦的告訴你"Object還不簡單嗎,就是所有類型的基類" 這個答案是對的,但是不足以說明Object真正是什麼 在這篇文章我們將會通過閱讀CoreCLR的源代碼瞭解Object在記憶體中的結構和 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...