基於Node.js的微信JS-SDK後端介面實現

来源:http://www.cnblogs.com/wuyuchang/archive/2017/07/14/7170949.html
-Advertisement-
Play Games

做了一個網站,放到線上,用微信打開,點擊分享,可是分享後發給朋友的鏈接卡片是微信預設自帶的,如下: 這標題,描述以及圖片是預設自帶的,醜不說,分享給別人還以為是盜號網站呢,而接入微信的JSSDK後,分享可以自定義內容,如下: 我承認,雖然這分享的標題和內容也並不正經,但這不妨礙我表達 我們可以通過微 ...


做了一個網站,放到線上,用微信打開,點擊分享,可是分享後發給朋友的鏈接卡片是微信預設自帶的,如下:



這標題,描述以及圖片是預設自帶的,醜不說,分享給別人還以為是盜號網站呢,而接入微信的JSSDK後,分享可以自定義內容,如下:



我承認,雖然這分享的標題和內容也並不正經,但這不妨礙我表達我們可以通過微信JSSDK定義分享內容,接下來我們將一步一步從零實現JSSDK從後端Node.js的接入。

成為測試公眾號開發者

登錄測試公眾號後臺

首先我們需要在微信公眾平臺申請測試介面,地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
使用微信掃描登錄後,即可來微信公眾平臺測試賬號系統。

成為測試公眾號開發者

其次在微信公眾平臺測試賬號中,掃描測試號二維碼,成為測試公眾號的開發者

介面配置信息

修改介面配置信息

  1. URL地址必須是你伺服器上的地址,此地址要能通過瀏覽器的地址欄訪問到(沒有伺服器?沒關係,一會兒我們搭建一個)
    假設我這裡填寫的伺服器地址是"http://www.your_server_name.com/wxJssdk"
  2. Token你可以隨意填寫,用作生成簽名,(不知道簽名?沒關係,一會兒會用到這東西的)
    假設我這裡填寫的Token是"jegfjaeghfuccawegfgjdbh"

此時點擊提交是會提示配置失敗的,因為在提交的時候,微信是會請求你的伺服器地址,而你的當前配置的地址並不能訪問,所以會提示配置失敗。不過別急,我們先來搭建一個簡單的Node伺服器,讓微信能夠訪問該伺服器。

搭建簡單的Node伺服器

我們需要在http://www.your_server_name.com 這個功能變數名稱上搭建一個伺服器,並且曝出一個介面為/wxJssdk

const express = require('express')
const app = express()

app.get('/wxJssdk', (req, res) => {
  res.send('請求成功了了了了')
})

app.listen(80, err => {
  if(!err) console.log('connect succeed')
})

現在我們在地址欄中訪問http://www.your_server_name.com/wxJssdk ,如果頁面顯示“請求成功了了了了”,則進入到下一步,如果沒有成功的話,檢查一下你的伺服器是否開啟Node伺服器,如:node index.js

此時保存微信測試公眾號後臺的介面配置信息,仍然會提示配置失敗,這是因為我們沒有按照它的要求返回。

根據微信測試公眾號請求信息返回對應內容

根據微信公眾號開發文檔接入指南,微信在請求我們配置的介面時,會帶上如下信息

參數 描述
signature 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。
timestamp 時間戳
nonce 隨機數
echostr 隨機字元串

微信伺服器會通過GET請求,來請求我們所配置的介面,並帶上以上表格的信息,而我們必須按照以下要求,將微信發送的信息進行要求校驗,以確保是微信發送的信息,其中校驗流程如下:

1)將token、timestamp、nonce三個參數進行字典序排序
2)將三個參數字元串拼接成一個字元串進行sha1加密
3)開發者獲得加密後的字元串可與signature對比,標識該請求來源於微信

const express = require('express')
const app = express()
const sha1 = require('sha1')

app.get('/wxJssdk', (req, res) => {
  let wx = req.query

  let token = 'jegfjaeghfuccawegfgjdbh'
  let timestamp = wx.timestamp
  let nonce = wx.nonce

  // 1)將token、timestamp、nonce三個參數進行字典序排序
  let list = [token, timestamp, nonce].sort()

  // 2)將三個參數字元串拼接成一個字元串進行sha1加密
  let str = list.join('')
  let result = sha1(str)

  // 3)開發者獲得加密後的字元串可與signature對比,標識該請求來源於微信
  if (result === wx.signature) {
    res.send(wx.echostr) // 返回微信傳來的echostr,表示校驗成功,此處不能返回其它
  } else {
    res.send(false)
  }
})

此時我們重啟Node伺服器,再次保存介面配置信息即可配置成功。

微信JSSDK使用步驟

根據微信JSSDK說明文檔,我們需要完成如下:

填寫安全功能變數名稱

登錄微信公眾平臺進入“公眾號設置”的“功能設置”里填寫“JS介面安全功能變數名稱”,即要調用介面的功能變數名稱,不包含協議

前端引入JS

在需要調用JS介面的頁面引入此JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

填寫介面的配置信息

wx.config({
  debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會列印。
  appId: '', // 必填,公眾號的唯一標識
  timestamp: , // 必填,生成簽名的時間戳
  nonceStr: '', // 必填,生成簽名的隨機串
  signature: '',// 必填,簽名
  jsApiList: [] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
});

調用介面

做你前端該做的,調用微信分享介面,或微信提供的其它介面,whatever you need,當然,這並不是我們所要講的重點,我們接下來要看一下微信的配置信息從哪獲取

在Node伺服器中生成jssdk所需要的配置信息

從上一節可以看到,調用微信JSSDK需要以下信息

  1. appId
  2. timestamp
  3. nonceStr
  4. signature
  5. jsApiList

其中:

  1. 第1項appId是測試公眾號後臺的appId,我們已知
  2. 第2項時間戳我們也可以自己生成
  3. 第3項nonceStr可以隨意填寫,你可以理解為密鑰
  4. 第4項signature則需要我們按要求生成
  5. 第5項是所需要介面的介面名

生成signature

生成簽名之前必須先瞭解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS介面的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。

為了保證我們appid,appsecret,nonceStr等信息不在前端曝露,我們以下步驟將在伺服器上進行操作,以免他人盜用信息獲取(註:微信請求有每日次數限制,一旦超出,則無法使用,具體請求次數限制在微信公眾號後臺中可查看)

生成access_token

根據微信開發文檔[獲取access_token文檔說明],我們需要將微信測試公眾號後臺的appid和和appsecret以GET的請求方式向https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 發起請求獲取token,請求成功後我們會獲得下返回JSON轉化的字元串

{"access_token":"ACCESS_TOKEN","expires_in":7200}

具體請求代碼如下:

const request = require('request')

const grant_type = 'client_credential'
const appid = 'your app id'
const secret = 'your app secret'

request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
  let access_toekn = JSON.parse(body).access_token
})

獲取jsapi_ticket

const request = require('request')

const grant_type = 'client_credential'
const appid = 'your app id'
const secret = 'your app secret'

request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
  let access_toekn = JSON.parse(body).access_token

  request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => {
     let jsapi_ticket = JSON.parse(body).ticket
  })
})

生成簽名

生成簽名的步驟和最開始的/wxJssdk的演算法是一致的,具體如下:

let jsapi_ticket = jsapi_ticket  // 上一步從獲取的jsapi_ticket
let nonce_str = '123456'    // 密鑰,字元串任意,可以隨機生成
let timestamp = new Date().getTime()  // 時間戳
let url = req.query.url   // 使用介面的url鏈接,不包含#後的內容

// 將請求以上字元串,先按字典排序,再以'&'拼接,如下:其中j > n > t > u,此處直接手動排序
let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '&timestamp=' + timestamp + '&url=' + url

// 用sha1加密
let signature = sha1(str)

連接後的代碼為:

const request = require('request')

const grant_type = 'client_credential'
const appid = 'your app id'
const secret = 'your app secret'

request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
  let access_toekn = JSON.parse(body).access_token

  request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => {
     let jsapi_ticket = JSON.parse(body).ticket
     let nonce_str = '123456'    // 密鑰,字元串任意,可以隨機生成
     let timestamp = new Date().getTime()  // 時間戳
     let url = req.query.url   // 使用介面的url鏈接,不包含#後的內容

     // 將請求以上字元串,先按字典排序,再以'&'拼接,如下:其中j > n > t > u,此處直接手動排序
     let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '&timestamp=' + timestamp + '&url=' + url

     // 用sha1加密
     let signature = sha1(str)
  })
})

曝露介面,返回給前端

app.post('/wxJssdk/getJssdk', (req, res) => {
  const request = require('request')

  const grant_type = 'client_credential'
  const appid = 'your app id'
  const secret = 'your app secret'

  request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
    let access_toekn = JSON.parse(body).access_token

    request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => {
       let jsapi_ticket = JSON.parse(body).ticket
       let nonce_str = '123456'    // 密鑰,字元串任意,可以隨機生成
       let timestamp = new Date().getTime()  // 時間戳
       let url = req.query.url   // 使用介面的url鏈接,不包含#後的內容

       // 將請求以上字元串,先按字典排序,再以'&'拼接,如下:其中j > n > t > u,此處直接手動排序
       let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '&timestamp=' + timestamp + '&url=' + url

       // 用sha1加密
       let signature = sha1(str)

       res.send({
         appId: appid,
         timestamp: timpstamp,
         nonceStr: nonce_str,
         signature: signature,
       })
    })
  })
})

前端請求後端介面,獲取配置信息

獲取配置

axios.post('/wxJssdk/getJssdk', {url: location.href}).then((response) => {
  var data = response.data

  wx.config({
    debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會列印。
    appId: data.appId, // 必填,公眾號的唯一標識
    timestamp: data.timestamp, // 必填,生成簽名的時間戳
    nonceStr: data.nonceStr, // 必填,生成簽名的隨機串
    signature: data.signature,// 必填,簽名,見附錄1
    jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
  });

})

做你想做的,比如,自定義分享

if (wx) {
  axios.post('/wxJssdk/getJssdk', {url: location.href}).then((response) => {
    var data = response.data

    wx.config({
      debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會列印。
      appId: data.appId, // 必填,公眾號的唯一標識
      timestamp: data.timestamp, // 必填,生成簽名的時間戳
      nonceStr: data.nonceStr, // 必填,生成簽名的隨機串
      signature: data.signature,// 必填,簽名,見附錄1
      jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
    });

    wx.ready(function () {
      wx.onMenuShareTimeline({
      title: wxShare.title,
      desc: wxShare.desc,
      link: wxShare.link,
      imgUrl: wxShare.imgUrl
      });

      wx.onMenuShareAppMessage({
      title: wxShare.title,
      desc: wxShare.desc,
      link: wxShare.link,
      imgUrl: wxShare.imgUrl
    });
  })

    wx.error(function (res) {
       // config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,也可以在返回的res參數中查看,對於SPA可以在這裡更新簽名。
    })
  })

}

至此,後端配置好了,我們已經能夠正常使用微信的介面了,但是微信每日介面請求是有上限的,通過2000次/天,因此如果網站上線後,一量當天訪問量超過2000次你的介面將失效,而且每次都請求微信介面兩次,造成請求時間浪費,所以我們需要將以上獲取信息緩存在後端,避免造成介面失效以及多次請求微信後臺。

緩存access_token及jsapi_ticket

此處直接上代碼,利用node_cache包進行緩存

const request = require('request')
const express = require('express')
const app = express()
const sha1 = require('sha1')
const waterfall = require('async/waterfall')
const NodeCache = require('node-cache')
const cache = new NodeCache({stdTTL: 3600, checkperiod: 3600}) //3600秒後過過期

app.get('/wxJssdk', (req, res) => {
  let wx = req.query

  // 1)將token、timestamp、nonce三個參數進行字典序排序
  let token = 'jegfjaeghfuyawegfgjdbh'
  let timestamp = wx.timestamp
  let nonce = wx.nonce

  // 2)將三個參數字元串拼接成一個字元串進行sha1加密
  let list = [token, timestamp, nonce]
  let result = sha1(list.sort().join(''))

  // 3)開發者獲得加密後的字元串可與signature對比,標識該請求來源於微信
  if (result === wx.signature) {
    res.send(wx.echostr)
  } else {
    res.send(false)
  }
})

app.get('/wxJssdk/getJssdk', (req, res) => {
  let grant_type = 'client_credential'
  let appid = 'your app id'
  let secret = 'your app secret' // appscret

  let steps = []

  // 第一步,獲取access_token
  steps.push((cb) => {

  let steps1 = []

    // 第1.1步,從緩存中讀取access_token
    steps1.push((cb1) => {
      let access_token = cache.get('access_token', (err, access_token) => {
        cb1(err, access_token)
      })
    })

    // 第1.2步,緩存中有access_token則直接返回,如果沒有,則從伺服器中讀取access_token
    steps1.push((access_token, cb1) => {
      if (access_token) {
        cb1(null, access_token, 'from_cache')
      } else {
        request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
          cb1(err, JSON.parse(body).access_token, 'from_server')
        })
      }
    })

    // 第1.3步,如果是新從伺服器取的access_token,則緩存起來,否則直接返回
    steps1.push((access_token, from_where, cb1) => {
      if (from_where === 'from_cache') {
        console.log(' === 成功從緩存中讀取access_token: ' + access_token + ' ===')
        cb1(null, access_token)
      } else if (from_where === 'from_server') {
        cache.set('access_token', access_token, (err, success) => {
          if (!err && success) {
            console.log(' === 緩存已過期,從伺服器中讀取access_token: ' + access_token + ' ===')
            cb1(null, access_token)
          } else {
            cb1(err || 'cache設置access_token時,出現未知錯誤')
          }
        })
      } else {
        cb1('1.3獲取from_where時,from_where值為空')
      }
    })



    waterfall(steps1, (err, access_token) => {
      cb(err, access_token)
    })
  })


  // 第二步,獲取ticket
  steps.push((access_token, cb) => {
    let steps1 = []

    // 第2.1步,從緩存中讀取ticket
    steps1.push((cb1) => {
      let ticket = cache.get('ticket', (err, ticket) => {
        cb1(err, ticket)
      })
    })

    // 第2.2步,緩存中有ticket則直接返回,如果沒有,則從伺服器中讀取ticket
    steps1.push((ticket, cb1) => {
      if (ticket) {
        cb1(null, ticket, 'from_cache')
      } else {
        request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => {
          cb1(err, JSON.parse(body).ticket, 'from_server')
        })
      }
    })

    // 第2.3步,如果新從伺服器取的ticket,則緩存起來,否則直接返回
    steps1.push((ticket, from_where, cb1) => {
      if (from_where === 'from_cache') {
        console.log(' === 成功從緩存中讀取ticket: ' + ticket + ' ===')
        cb1(null, ticket)
      } else if (from_where === 'from_server') {
        cache.set('ticket', ticket, (err, success) => {
          if (!err && success) {
            console.log(' === 緩存已過期,從伺服器中讀取ticket: ' + ticket + ' ===');
            cb1(null, ticket)
          } else {
            cb1(err || 'cache設置ticket時,出現未知錯誤')
          }
        })
      } else {
        cb1('2.3獲取from_where時,from_where值為空')
      }
    })

    waterfall(steps1, (err, ticket) => {
      cb(err, ticket)
    })
  })


  // 第三步,生成簽名
  steps.push((ticket, cb) => {
    let jsapi_ticket = ticket
    let nonce_str = '123456'
    let timestamp = new Date().getTime()
    let url = req.query.url

    let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '&timestamp=' + timestamp + '&url=' + url
    let signature = sha1(str)

    cb(null, {
      appId: appid,
      timestamp: timestamp,
      nonceStr: nonce_str,
      signature: signature,
      ticket: ticket
    })
  })

  waterfall(steps, (err, data) => {
    if (err) {
      res.send({status: 'error', data: err})
    } else {
      res.send({status: 'success', data: data})
    }
  })
})

app.use('/wxJssdk/public', express.static('public'))

app.listen(80, err => {
  if(!err) console.log('connect succeed')
})

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

-Advertisement-
Play Games
更多相關文章
  • 面試問到這個··答不出來就是沒有架構能力···這裡學習一下···面試的時候直接讓我說出26種設計模式··當時就懵逼了··我記得好像之前看的時候是23種的 還有3個是啥的··· 這裡先列出幾種創建型模式,工廠、抽象工廠、單例,建造者、原型,後續在更新 工廠模式:缺點是每增加一個類型就得增加一個工具類和 ...
  • 前面的話 每次寫HTML結構涉及到CSS命名時,都要掙扎一番。關於CSS命名的規範,市面上有不少,如OOCSS、SMACSS、BEM和MVCSS等。在這裡面最火的應該算BEM了。本文將詳細介紹CSS命名 主流命名 【BEM】 說起CSS命名,當然要提到BEM。BEM的意思就是B模塊(block)、E ...
  • 工作中遇到的功能點,覺得以後可以復用,就打算備一下咯 格式要求(例):On Friday, July 14, 2017 我的思路是用js就可以實現了,具體代碼如下: ...
  • 前面的話 由歷史原因及個人習慣引起的 DOM 結構、命名不統一,導致不同成員在維護同一頁面時,效率低下,迭代、維護成本極高。所以,使用統一的命名規範非常必要。本文將詳細介紹命名規範 目錄命名 1、項目文件夾:projectname 2、樣式文件夾:css 3、腳本文件夾:js 4、樣式類圖片文件夾: ...
  • 初始化本地git倉庫(創建新倉庫) git init 配置用戶名 git config --global user.name "xxx" 配置郵件 git config --global user.email "[email protected]" git status等命令自動著色 git config -- ...
  • 假如同一個標簽被多個選擇器選中,每個選擇器都設置了相同的樣式,瀏覽器中載入時這個樣式聽誰的? 不同選擇器設置的同一個樣式,只會選擇一個進行載入,不會疊加。 為瞭解決聽誰的問題,引入層疊性的概念。 層疊性:多個選擇器選中同一個標簽,設置同一個樣式,瀏覽器中載入時,不會載入所有的屬性值,挑選其中一個載入 ...
  • 以下兩種方法都是將兩個網頁放在同一個文件夾里,是同級文件。 一、HTML 鏈接是通過 <a> 標簽進行定義的 (這裡的網頁命名方式不准確,需根據特定情況命名) 二、另一種鏈接是通過JavaScript實現 (這種方法需要引用jquery,jquery需要下載,,我在圖片上會用紅圈畫出引用的jquer ...
  • 什麼是LESSCSS LESSCSS是一種動態樣式語言,屬於CSS預處理語言的一種,它使用類似CSS的語法,為CSS的賦予了動態語言的特性,如變數、繼承、運算、函數等,更方便CSS的編寫和維護。 LESSCSS可以在多種語言、環境中使用,包括瀏覽器端、桌面客戶端、服務端。 語言特性快速預覽: 變數: ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...