UWP 應用中的語音識別和合成

来源:http://www.cnblogs.com/jinchen/archive/2016/08/29/uwp-yuyin.html
-Advertisement-
Play Games

在 UWP 的開發過程中,我們可能需要提供多種交互方式,例如滑鼠、鍵盤、觸摸、游戲手柄等,當然,語音也是一項很重要的功能。 眾所周知,在 Windows 中的許多個版本都包含有語音功能,特別是在 Windows 10 上,Cortana(小娜)更是非常智能。同時,對於開發者而言,我們也能非常方便的在 ...


在 UWP 的開發過程中,我們可能需要提供多種交互方式,例如滑鼠、鍵盤、觸摸、游戲手柄等,當然,語音也是一項很重要的功能。

眾所周知,在 Windows 中的許多個版本都包含有語音功能,特別是在 Windows 10 上,Cortana(小娜)更是非常智能。同時,對於開發者而言,我們也能非常方便的在其中融入我們的功能,不過本文並不是想說這個。這裡將介紹如何開發我們自己的 UWP 應用的語音交互,即,在我們的 UWP 內部,支持用戶的語音命令和語音輸入,並提供語音反饋。

準備工作

首先,在 Visual Studio 2015 Update 3 或更高版本中,創建一個 UWP 項目。併在 Package.appxmainfest 中,在 Capabilities 中勾選“麥克風”,或者直接用文本編輯器打開該文件,在 Capabilities 節點中,插入以下代碼。

<DeviceCapability Name="microphone" />

打開 MainPage.xaml.cs 文件,我們需要先在其中加入以下命名空間,這些將分別用於處理語音識別、語音合成和文件訪問。

using Windows.Media.SpeechRecognition;
using Windows.Media.SpeechSynthesis;
using Windows.Storage;

語音識別

現在,我們需要在 MainPage 類里實現一個方法,用於執行語音識別,並返回結果。在處理語音識別的過程當中,需要用到一個名為 SpeechRecognizer 的類所創建的實例,該實例可被覆用,以便多次處理語音識別任務。因此,我們還需要一個欄位來存儲這個實例,併在該方法首次調用時,初始化這個實例。另外,由於該實例可以添加一些語音識別約束,用於描述場景,所以在使用前,需要先對這些約束進行編譯,即使並沒有添加任何約束。該編譯過程和語音識別都是非同步的,因此我們需要將這個方法也聲明為非同步方法。

private SpeechRecognizer _speechRecognizer;

private async Task<SpeechRecognitionResult> SpeechRecognizeAsync() {
    if (_speechRecognizer == null)
    {
        // 創建一個 SpeechRecognizer 實例。
        _speechRecognizer = new SpeechRecognizer();

        // 編譯聽寫約束。
        await _commandSpeechRecognizer.CompileConstraintsAsync();
    }

    // 開始語音識別,並返回結果對象。
    return await _speechRecognizer.RecognizeAsync();
}

另外,也可以在這個方法里初始化 _speechRecognizer 欄位的地方,對該實例的一些行為和狀態做一些控制,例如使用其 RecognitionQualityDegrading 事件監聽語音輸入的質量等。

現在,我們可以在界面中添加一個按鈕,綁定一個點擊事件,用以測試語音錄入功能。

<Button x:Name="SpeechButton" Content="語音" Click="Speech_Click" />

在綁定的點擊事件中,需要先調用前面寫的 SpeechRecognizeAsync() 成員方法獲取到識別後的結果,然後在識別後,出於測試目的,我們彈出一個對話框,顯示所識別的文本內容。在實際使用場景中,這個結果應該是被輸入到文本框,或者是用在其它場合。

private async void Speech_Click(object sender, RoutedEventArgs e)
{
    // 獲取語音識別結果。
    var speechRecognitionResult = await SpeechRecognizeAsync();

    // 彈出一個對話框,用於展示識別出來的文本。
    var messageDialog = new Windows.UI.Popups.MessageDialog(speechRecognitionResult.Text, "你剛說了");
    await messageDialog.ShowAsync();
}

現在,按下 F5 運行,可以發現,界面上有一個按鈕“語音”,點擊後,說出一句話,稍等片刻,即會彈出一個標題為“你剛說了”的對話框,裡面包含了剛纔所說的內容。

使用圖形輸入界面

在剛纔的場景中,點一下按鈕後,其實界面看似並沒有任何反應,但其實已經開始進入聆聽我們說話的過程中了,這樣的用戶體驗並不好,可能需要加入一些提示,例如提示用戶開始聆聽等。

不過,其實系統也有提供預設的通用界面可供調用。這些界面雖然較為簡單,但在許多場景下也的確滿足需求。系統預設的界面會在開始聆聽時彈出一個對話框,顯示“正在聆聽”併發出提示音;當聆聽結束時,會在該彈出框中顯示所聆聽到的內容,並同時用合成語音進行反饋;而失敗時,也會在該彈出框中顯示出錯信息,並提供合成語音反饋。

使用系統預設的語音識別界面,僅需把 SpeechRecognizeAsync() 成員方法中最後一步的 speechRecognizer.RecognizeAsync() 調用,替換成 speechRecognizer.RecognizeWithUIAsync() 即可。

// 開始識別,並呈現系統預設界面。
return await _speechRecognizer.RecognizeWithUIAsync();

如果需要調整系統預設的界面中的內容,例如“正在聆聽”的顯示文案、示例提示等信息等信息,也可以在初始化 _speechRecognizer 欄位時,在執行編譯過程之前,即調用 await _speechRecognizer.CompileConstraintsAsync(); 方法之前,通過對該欄位的 UIOptions 屬性進行設置來控制。

// 可以設置“正在聆聽”期間的文案。
_speechRecognizer.UIOptions.AudiblePrompt = "請問你打算要我做什麼?";

// 可以設置“正在聆聽”期間顯示的示例提示,用於提示用戶可以說什麼。
_speechRecognizer.UIOptions.ExampleText = "請嘗試說:開燈、關燈。";

// 當聆聽結束後,可以設置是否要通過合成語音讀出聽到的結果。
_speechRecognizer.UIOptions.IsReadBackEnabled = false;

// 可以設置是否要顯示聆聽成功後的確認消息框。
_speechRecognizer.UIOptions.ShowConfirmation = false;

除此之外,也可以對該欄位的 Timeouts 成員屬性里的屬性進行設置,以控制各類時長。

語音輸入場景

為了更精準的識別所期待的內容,如前面所說,SpeechRecognizer 類支持添加語音識別約束。語音識別至少需要一個約束,才能定義可識別的辭彙。如果未指定任何約束,將使用通用 Windows 應用的預定義聽寫語法。

語音識別約束必須實現 ISpeechRecognitionConstraint 介面,然後在 SpeechRecognizer 類的 Constraints 成員屬性中調用 Add 成員方法添加至約束列表中。可以添加多項語音識別約束,但是有的可能會有衝突。

通常情況下,可能會應用到以下這些內置約束。

  • SpeechRecognitionTopicConstraint - 基於預定義語法的約束。
  • SpeechRecognitionListConstraint - 基於字詞或短語列表的約束。
  • SpeechRecognitionGrammarFileConstraint - 在語音識別語法規範(SRGS)文件中定義的約束。

添加這些約束必須在編譯約束之前,即必須放置於上方示例代碼中的 SpeechRecognizeAsync() 成員方法里 await _commandSpeechRecognizer.CompileConstraintsAsync(); 調用之前。

語音識別約束中有一個 Tag 成員屬性,用於作為該約束的 ID,設置後,在之後的語音識別結束時,可以用於判斷時基於哪個約束進行識別的。例如,可能添加了多個識別約束,每個約束都有不同的預期以及對應的處理方式,當語音識別結束時,我們需要知道當前用戶所說的內容,是符合哪一個約束所對應的預期的,這樣我們才知道接下來應該如何處理其語音輸入的內容,這時,可以根據 ID 來進行簡單的判斷,因為語音識別結果 SpeechRecognitionResult 類中,會包含 Constraint 屬性,這個屬性即是之前所實際採用的約束。當然,也可以直接判斷約束實例本身,而非通過這個 ID 來區分。不過需要註意的是,結果中的這個屬性可能為空,所以可能要處理為空的可能。

指定預定義語法的約束

SpeechRecognitionTopicConstraint 類用於預期為 Web 搜索、聽寫或表單輸入類型,使用方式如下。

var webGrammar = new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario.WebSearch, "webSearch", "web");
_speechRecognizer.Constraints.Add(webGrammar);

這個類的構造函數支持輸入3個參數,其中最後一個為選填。

  • 第1個參數是場景類型,為一個枚舉。
  • Web 搜索(SpeechRecognitionScenario.WebSearch = 0)。
  • 聽寫(SpeechRecognitionScenario.Dictation = 1)。
  • 表單輸入(SpeechRecognitionScenario.FormFilling = 2)。
  • 第2個參數為具體子類型,例如,針對錶單輸入,可以定義為支持拼寫檢查的文本類型、日期類型、姓名類型等,詳見 MSDN 文檔
  • 第3個參數是該約束項的 ID,會被填入 Tag 成員屬性當中去,用於之後在判斷用戶所說的內容是在哪個約束中識別的作為依據。

指定編程列表約束

SpeechRecognitionListConstraint 類用於字詞或短語列表的語音識別約束,可以通過程式輸出一個字元串列表,然後添加至約束列表。

例如,假設我們需要添加一項功能,提供播放音樂和暫停的功能,我們可以至少添加2個這類約束,並刪除前面註冊預定義語法約束的代碼,以防止衝突。

// 獲取歌曲列表。
// 此處硬編碼了一些歌曲,實際應該通過程式去歌曲庫中讀取。
var songs = new [] { "曲目一", "另一首歌", "未命名歌曲" };

// 生成接受的語音列表。
var playCmds = songs.Select((item) => {
    return string.Format("{0}{1}", "播放", item;
});

// 創建約束,並將預期的語音列表作為構造函數的參數傳入。
// 同時,可以選填其 Tag。
var playConstraint = new SpeechRecognitionListConstraint(playCmds, "play");

// 添加約束。
_speechRecognizer.Constraints.Add(playConstraint);

// 接下來,創建暫停和繼續播放所對應的功能,並添加該約束。
var pauseConstraint = new SpeechRecognitionListConstraint(new [] { "暫停", "繼續播放" }, "pauseAndResume");
_speechRecognizer.Constraints.Add(pauseConstraint);

至於應該根據何種情況來設定預期的語音指令,這需要根據現實的使用場景和整體的程式設計來進行規劃,許多情況下,並不需要像這個示例一樣使用2個約束實例,僅用1個也可以。此處示例僅為說明這個約束的使用方式。

指定 SRGS 語法約束

SpeechRecognitionGrammarFileConstraint 類用於引入一個 SRGS 文件作為語音識別約束。例如下方這樣,我們假設我們需要控制一個燈的開關,我們需要先讀取一個 SRGS 類型的文件(.grxml),然後作為該類的第1個參數傳入,第2個參數為可選的 Tag(即 ID)。

// 獲取文件。
var grammarFile = await GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Light.grxml"));

// 創建 SRGS 文件約束。
var srgsConstraint = new SpeechRecognitionGrammarFileConstraint(grammarFile, "light");

// 添加約束。
_speechRecognizer.Constraints.Add(srgsConstraint);

接下來,需要去實現那個位於項目中 Assets 目錄下的 Light.grxml 文件。

SRGS 語法

在項目的 Assets 目錄下新建一個名為 Light.grxml 的文件,併在文件屬性中,將“數據包操作”設置為“內容”,以及將“複製到輸出目錄”則設置為“始終複製”。該文件里的內容需要符合 SRGS 語法。SRGS 是一套為語音識別創建 XML 格式語法的行業標準標記語言,可以處理較複雜的語音識別。該文件需要包含類似下方的 XML 節點。

<grammar xml:lang="zh-Hans" root="Control" version="1.0"  tag-format="semantics/1.0" xmlns="http://www.w3.org/2001/06/grammar">
    <rule id="Control">
    </rule>
</gramma>

在這裡,grammar 根節點里的 xml:lang 屬性,用於定義這個 SRGS 文件所支持的語言,只有噹噹前語音識別的語音於這個值匹配的時候,該文件中描述的語音識別規則才有效;而 root 屬性里需要定義一個規則的 ID,在 grammar 根節點里可以定義多套規則,但是入口只有1個,即此屬性中定義的;其它屬性可參考 MSDN 文檔。在 grammar 根節點裡面,必須至少有一個 rule 節點,並需要定義其 id 屬性,用於檢索。id 值和 grammar 根節點 root 屬性定義的值相同的 rule 節點,即為此文件所指定的主規則;其它規則通常是被用於引用的。rule 節點中所支持的屬性可參閱 MSDN 文檔

由於我們計劃控制燈的開關,首先,需要一項規則是打開燈,因此,我們在 grammar 根節點中新建一個 rule 節點,並指定一個 ID TurnOn,然後在裡面來實現這個規則。具體的實現方式是,在裡面添加一個 item 節點,該節點的目的是指定預期可能說的話。

<rule id="TurnOn">
    <item>開燈</item>
</rule>

然而,日常生活中,打開燈可能又多種說法,除了“開燈”外,可能還有“打開燈”、“把燈打開”等,像這一類屬於多種可能都通向一個結果的,可以採用 one-of 節點將其包起來。於是我們把這個節點修改為如下。

<rule id="TurnOn">
    <one-of>
        <item>打開</item>
        <item>開燈</item>
        <item>亮燈</item>
        <item>打開燈</item>
        <item>把燈打開</item>
        <item>快開燈</item>
    </one-of>
</rule>

事實上,有些人很禮貌,即使和電腦說話,也可能會帶上“請”之類的敬詞,因此,我們還需要對其進行改造以下,在這個多選一的預期的前面,添加可能會說的內容。由於不確定是否會說,因此我們可以通過添加一個 item 節點,並通過其 repeat 屬性來設置可能的重覆次數來做到,由於最多可能只會說一個“請”字,那麼重覆次數的範圍應該是0到1之間。經過優化,最後該節點可能如下所示。

<rule id="TurnOn">
    <item repeat="0-1"></item>
    <item repeat="0-1">幫我</item>
    <item>
        <one-of>
            <item>打開</item>
            <item>開燈</item>
            <item>亮燈</item>
            <item>打開燈</item>
            <item>把燈打開</item>
            <item>快開燈</item>
        </one-of>
    </item>
</rule>

item 節點中還有一些其它屬性,可參閱 MSDN 文檔

完成了開燈,那麼還有關燈。同理,如下。

<rule id="TurnOff">
    <item repeat="0-1"></item>
    <item repeat="0-1">幫我</item>
    <item>
        <one-of>
            <item>關閉</item>
            <item>關燈</item>
            <item>關上</item>
            <item>把燈關了</item>
            <item>把燈關上</item>
            <item>快關燈</item>
        </one-of>
    </item>
</rule>

不過,寫到這裡,這些規則並未能被調用,因為剛纔說到,只有 root 指定的規則才是入口規則,所以我們需要改寫前面的 Control 規則。改寫的內容其實很簡單,也是一個多選一的情況,只要說出開燈或關燈的任一命令,那麼就執行。但如何指定執行哪個已定義的規則呢?可以使用 ruleref 節點,其 uri 屬性用於指定是引用哪個規則,其中的值採用類似 CSS 中的選擇器的語法方式,例如,用 # 開頭即表示後面跟著的是對應的 ID。

<rule id="Control">
    <one-of>
        <item>
            <ruleref uri="#TurnOn"/>
        </item>
        <item>
            <ruleref uri="#TurnOff"/>
        </item>
    </one-of>
</rule>

現在,寫完了 SRGS 文件。

識別 SRGS 語法約束的識別結果

當語音識別進入 SRGS 後,並得出結果,我們需要知道在執行結束後識別最後走進了哪一個規則,因為只有如此,才能決定接下來執行什麼實際的操作。與前面兩個約束不同,前面的兩個約束可以通過所執行到的約束類型和簡單分析識別到的語句來進行判斷;這個 SRGS 的結果顯得更為複雜。首先,我們需要根據結果返回的約束類型來判斷,是否識別出的結果為這個約束下的;然後,我們可以獲取其所執行的規則路徑,來明白其所識別的邏輯結果。

規則路徑其實是個字元串列表,即我們之前在 rule 中定義的 ID 的順序執行列表,例如,當我們說出“開燈”時,其返回的時 ["Control", "TurnOff"]。我們來新寫一個方法來處理這個結果。由於第1個規則肯定時根節點,因此我們在這個示例中,只需要簡單判斷第2個路徑是什麼即可。

private Control(IList<string> path)
{
    if (path.Count < 2) return;
    switch (path[1])
    {
        case "TurnOn":
            // Turn on.
            break;
        case "TurnOff":
            // Turn off.
            break;
    }
}

然後,我們還需要改寫前面所寫的 Speech_Click() 方法,以在該 SRGS 約束被執行到時調用上述方法。

// 獲取語音識別結果。
var speechRecognitionResult = await SpeechRecognizeAsync();

// 根據所執行到的約束來決定執行的內容。
if (speechRecognitionResult.Constraint == null) return;
switch (speechRecognitionResult.Constraint.Tag)
{
    case "light":
        Control(speechRecognitionResult.RulePath.ToList());
        break;
    case "play":
        // 播放某首歌。
        var songName = speechRecognitionResult.Text.Substring("播放".Length).Trim();
        break;
    case "pauseAndResume":
        // 暫停或繼續播放。
        break;
}

語音合成

語音識別是將用戶說的話識別為文本或指令,同時,有的時候,我們又需要反過來,通過語音合成,將指定的內容通過聲音的形式反饋給用戶。例如,當用戶通過語音發出了某項指令後,電腦通過語音進行回覆,以模擬一個語音對話的模式。

在 UWP 中,如果希望播放出某一段聲音,其中一種做法是,在界面中先嵌入一個 MediaElement 控制項,然後在通過控制這個對象來播放聲音。例如,在 MainPage.xaml 文件中插入該控制項。

<MediaElement x:Name="mediaElement"/>

接下來,我們需要通過語音合成,來將一段指定的文本,以音頻的形式讓電腦朗讀出來。我們新寫一個方法,用於將一段文本轉為語音,並通過剛纔新建的 MediaElement 控制項播放出來。

private SpeechSynthesizer _synth = new SpeechSynthesizer();

private async void Speak(string value)
{
    // 創建一個文本轉語音的流。
    var stream = await synth.SynthesizeTextToStreamAsync(value);

    // 將流發送到 MediaElement 控制項並播放。
    mediaElement.SetSource(stream, stream.ContentType);
    mediaElement.Play();
}

然後我們可以對前面所寫的 Control(IList<string> path) 成員方法中的內容進行改寫。

private Control(IList<string> path)
{
    if (path.Count < 2) return;
    switch (path[1])
    {
        case "TurnOn":
            // Turn on.
            Speak("燈已打開。");
            break;
        case "TurnOff":
            // Turn off.
            Speak("燈已關上。");
            break;
    }
}

現在,當你點擊界面上的“語音”按鈕後,在提示聆聽後,說出“開燈”,稍後便會聽到“燈已打開”的提示。

定製語音合成

剛剛的語音播放其實採用的是正常模式,但有時可能需要對內容的語音播放進行細節上的控制,例如調整音調、重讀、語速等,甚至字或詞的發音。這時候,我們需要用到語音合成標記語言 SSML 來進行處理。例如以下示例。

// 創建一個字元串,包含 SSML 描述內容,用於朗讀。
string Ssml =
    @"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-US'>" +
    "<prosody rate='slow'>你好嗎?</prosody> " + 
    "<break time='500ms'/>" +
    "</speak>";

// 用合成器根據文本創建音頻流。
var stream = await _synth.synthesizeSsmlToStreamAsync(Ssml);

// 將音頻播放出來。
mediaElement.SetSource(stream, stream.ContentType);
mediaElement.Play();

節點 prosody 用於控制朗讀的語速、音調、音量等,詳見 MSDN 文檔


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

-Advertisement-
Play Games
更多相關文章
  • 效果: ...
  • js中的NaN不和任何值相等,包括自身。 所以可以使用x!=x來判斷x是否是NaN,當且僅當x為NaN時,表達式的結果為true。 可以依此刪除數組中的'NaN'。 ...
  • $("cli-open").click(function(){ var scrollTop = document.body.scrollTop;//保存點擊前滾動條的位置 window.onscroll=function(){ document.body.scrollTop = scrollTop; ...
  • 需要給剛體添加一個自定義的屬性:m_customGravity,這樣就可以動態的修改每一個剛體自定義的重力,查找box2d源碼大約在5486行,加上紅色的一句代碼 使用的方法://創建一個圓形剛體var circle = new Circle({x:mousePoint.x,y:mousePoint ...
  • 不啰嗦上代碼: 怎麼調用不用我說了吧,看返回的4個方法: addTranEvent,addAnimEvent,removeAnimEvent,setStyleAttribute ...
  • 第二版 (-1)寫在前面 本文不是要詳細說明Validation插件的使用,而是將滿足開發需求的代碼已最應該使用的方式寫出來,並附有詳細的註釋 想要瞭解更多,去jquery的官網,有最新,最全面的,後續可能會寫怎麼從jquery官網獲得信息的博文 (0)資源配置 官網的jar包: lib 有該插件所 ...
  • 如何簡單相容性的實現父元素是半透明背景色,而子元素不受影響。 相容所有瀏覽器的背景顏色半透明CSS代碼: 註意:startColorStr 和 endColorStr 的值,前兩位是十六進位的透明度,後面六位是十六進位的顏色。 其格式為 #AARRGGBB 。 AA 、 RR 、 GG 、 BB 為 ...
  • 工具: Visual Studio 2015 update 3 Asp.Net Core 1.0 1 準備工作 申請微信公眾平臺介面測試帳號,申請網址:(http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login)。申請介面測試號無需公 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...