Nginx集群之基於Redis的WebApi身份驗證

来源:http://www.cnblogs.com/yongfeng/archive/2017/12/13/8029466.html
-Advertisement-
Play Games

Nginx在集群上使用Redis資料庫進行身份驗證,達到了支持集群、分散式。在此基礎上能夠實現單點登錄、時效性的訪問,結合WebApi最大限度地發揮了後臺身份驗證的管理Nginx集群使用Redis資料庫,客戶端利用 http basic身份驗證,訪問WebApi獲得Token並將Token存儲到Re... ...


目錄

1       大概思路... 1

2       Nginx集群之基於Redis的WebApi身份驗證... 1

3       Redis資料庫... 2

4       Visualbox虛擬機ubuntu下的redis部署... 3

5       編寫.NET WebApi的OnAuthorization身份驗證... 6

6       編寫.NET WebApi的ActionFilterAttribute令牌驗證... 8

7       編寫.NET WebApi的服務端... 10

8       編寫.NET WebApi的客戶端... 11

9       部署WebApi到本機... 17

10     Nginx集群配置搭建... 18

11     運行結果... 19

12     總結... 20

1       大概思路

l  Nginx集群之基於Redis的WebApi身份驗證

l  Redis資料庫

l  Visualbox虛擬機ubuntu下的redis部署

l  編寫.NET WebApi的OnAuthorization身份驗證

l  編寫.NET WebApi的ActionFilterAttribute令牌驗證

l  編寫.NET WebApi的服務端

l  編寫.NET WebApi的客戶端

l  部署WebApi到本機

l  Nginx集群配置搭建

l  運行結果

l  總結

2       Nginx集群之基於Redis的WebApi身份驗證

Nginx在集群上使用Redis資料庫進行身份驗證,達到了支持集群、分散式。在此基礎上能夠實現單點登錄、時效性的訪問,結合WebApi最大限度地發揮了後臺身份驗證的管理。

基於Redis的原因有幾個:第一點是Redis是基於記憶體的資料庫訪問起來比較快,能夠設置資料庫存儲的時效性;第二點,Redis資料庫的語法比較簡單、輕巧,在Linux、Windows伺服器均可以一鍵安裝完成;第三點,Redis支持數據的備份,即master-slave模式的數據備份。

以下是本文講述的主要結構圖:

客戶端輸入用戶名密碼伺服器,通過了用戶名密碼驗證,其中一臺WebApi伺服器生成一個Token並返回https的響應。客戶端收到Token保存在本地,帶上token發出ajax請求、WebRequest請求,經過Action過濾器的檢驗,訪問Action並返回數據。

Token令牌身份驗證機制:

Nginx集群之基於Redis的WebApi身份驗證,如下圖所示:

3       Redis資料庫

Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。它通常被稱為數據結構伺服器,因為值(value)可以是 字元串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。

具體可以在以下網址進行學習:

http://www.runoob.com/redis/redis-tutorial.html

 

4       Visualbox虛擬機ubuntu下的redis部署

(已安裝Oracle VM VirtualBox、ubuntu-17.10-desktop-amd64.iso)

Redis安裝可參考:http://www.runoob.com/redis/redis-install.html

 

  • 虛擬機

因網路的限制,本文采取的是“網路地址轉換(NAT)”方式,進行redis資料庫連接訪問。有條件的可以採用“橋接網上”的方式,便可以使多台PC機在同一redis資料庫進行訪問,達到集群的效果。

Linux的ubuntu打開6379埠:

sudo iptables -I INPUT -p tcp --dport 6379 -j ACCEPT

具體如下所示:

登錄Redis伺服器,設置密碼:

redis-cli
config set requirepass 123456

  • 主機

Windows安裝Telnet客戶端,進行測試連接成功與否

運行CMD,輸入命令行如下:

Microsoft Windows [版本 6.1.7601]
版權所有 (c) 2009 Microsoft Corporation。保留所有權利。

C:\Users\zhyongfeng>telnet 10.93.85.66 6379

測試成功

5       編寫.NET WebApi的OnAuthorization身份驗證

CustomAuthorizeAttribute.cs

using System.Web.Http;
using System.Web.Http.Controllers;

namespace SSLWebApi.Controllers
{
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //判斷用戶是否登錄
            if (actionContext.Request.Headers.Authorization != null)
            {
                string userInfo = System.Text.Encoding.Default.GetString(System.Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
                //用戶驗證邏輯  
                if (string.Equals(userInfo, string.Format("{0}:{1}", "zhyongfeng", "123456")))
                {
                    IsAuthorized(actionContext);
                }
                else
                {
                    HandleUnauthorizedRequest(actionContext);
                }
            }
            else
            {
                HandleUnauthorizedRequest(actionContext);
            }
        }
        protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
            challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
            throw new System.Web.Http.HttpResponseException(challengeMessage);
        }
    }
}

生成Token

BaseController.cs

using ServiceStack.Redis;
using SSLWebApi.Models;
using System;
using System.Web;
using System.Web.Http;

namespace SSLWebApi.Controllers
{
    /// <summary>
    /// BaseController繼承BaseController則需要身份驗證
    /// </summary>
    [CustomAuthorize]
    [RoutePrefix("api/Base")]
    public class BaseController : ApiController
    {
        [HttpGet]
        [Route("Login")]
        public string Login(string userId)
        {
            if (HttpRuntime.Cache.Get(userId) == null)
            {
                return CreateToken(userId);
            }
            else
            {
                HttpRuntime.Cache.Remove(userId);
                return CreateToken(userId);
            }
        }

        /// <summary>
        /// 生成token
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        private string CreateToken(string userId)
        {
            Token token = new Token();
            token.UserId = userId;
            token.SignToken = Guid.NewGuid();
            token.Seconds = 12;
            token.ExpireTime = DateTime.Now.AddSeconds(token.Seconds);

            //redis使用sudo iptables -A INPUT -p tcp --dport 6379 -j ACCEPT打開6379的埠
            //visualbox虛擬機使用“設置->網路->高級->埠轉發”創建商品規則
            //連接遠程visualbox虛擬機ubtuntu的redis,形成一個分散式的登錄驗證
            string remoteIp = "10.93.85.66";
            int remotePort = 6379;
            string remoteDbPassword = "123456";
            //RedisClient(地址,埠,密碼,0)
            using (var client = new RedisClient(remoteIp, remotePort, remoteDbPassword, 0))
            {
                string strToken = token.SignToken.ToString();
                if (client.Exists(userId) > 0)
                    client.Del(userId);
                if (client.Add(userId, strToken))
                {
                    //設置key的過期時間為12s
                    client.Expire(userId, 12);
                    return token.SignToken.ToString();
                }
                else
                    return string.Format("遠程虛擬機{0}的redis創建token失敗", remoteIp);
            }
        }
    }
}

6       編寫.NET WebApi的ActionFilterAttribute令牌驗證

WebApiSecurityFilter.cs

using ServiceStack.Redis;
using SSLWebApi.Models;
using System;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace SSLWebApi.Filter
{
    public class WebApiSecurityFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {

            if (actionContext.ActionDescriptor.ActionName == "Login")
            {
                //登錄成功則生成token
                base.OnActionExecuting(actionContext);
                return;
            }
            else
            {
                //判斷token令牌
                HttpRequestMessage request = actionContext.Request;
                string staffid = request.Headers.Contains("userid") ? HttpUtility.UrlDecode(request.Headers.GetValues("userid").FirstOrDefault()) : string.Empty;
                string timestamp = request.Headers.Contains("timestamp") ? HttpUtility.UrlDecode(request.Headers.GetValues("timestamp").FirstOrDefault()) : string.Empty;
                string nonce = request.Headers.Contains("nonce") ? HttpUtility.UrlDecode(request.Headers.GetValues("nonce").FirstOrDefault()) : string.Empty;
                string signature = request.Headers.Contains("signature") ? HttpUtility.UrlDecode(request.Headers.GetValues("signature").FirstOrDefault()) : string.Empty;

                if (String.IsNullOrEmpty(staffid) || String.IsNullOrEmpty(timestamp) || String.IsNullOrEmpty(nonce) || String.IsNullOrEmpty(signature))
                {
                    //令牌檢驗不通過
                    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                    return;
                }
                else
                {
                    string remoteIp = "10.93.85.66";
                    int remotePort = 6379;
                    string remoteDbPassword = "123456";
                    using (var client = new RedisClient(remoteIp, remotePort, remoteDbPassword, 0))
                    {
                        //令牌檢驗是否存在這個用戶
                        if (client.Exists(staffid) > 0)
                        {
                            string tempStr = System.Text.Encoding.UTF8.GetString(client.Get(staffid));
                            if (tempStr.Trim('"').Equals(signature))
                            {
                                //時間轉換成功、時間有效、token值相等
                                //令牌通過
                                base.OnActionExecuting(actionContext);
                                return;
                            }
                            else
                            {
                                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                                return;
                            }
                        }
                        else
                        {
                            actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                            return;
                        }
                    }
                }
            }
        }

    }
}

 

7       編寫.NET WebApi的服務端

UserController.cs

using System;
using System.Net;
using System.Web.Http;

namespace SSLWebApi.Controllers
{
    [RoutePrefix("api/User")]
    public class UserController : ApiController
    {
        /// <summary>
        /// 獲取當前用戶信息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("PostMessage")]
        public string PostMessage(dynamic obj)
        {
            return string.Format("當前輸入的消息是:{0}", Convert.ToString(obj.msg));
        }

        [Route("GetMachine")]
        public string GetMachine()
        {
            string AddressIP = string.Empty;
            foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            {
                if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
                {
                    AddressIP = _IPAddress.ToString();
                }
            }
            return string.Format("當前WebApi部署的IP是:{0}", AddressIP);
        }
    }
}

8       編寫.NET WebApi的客戶端

WebApiHelper.cs

using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Text;

namespace SSLWebApiClient.Common
{
    public class WebApiHelper
    {
        /// <summary>
        /// Post請求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url">url</param>
        /// <param name="data">數據</param>
        /// <param name="userid">帳戶</param>
        /// <param name="signature">數字簽名</param>
        /// <returns></returns>
        public static string Post(string url, string data, string userid, string signature)
        {
            return PostData(url, data, userid, signature);
        }

        /// <summary>
        /// Post請求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url">url</param>
        /// <param name="data">數據</param>
        /// <param name="userid">帳戶</param>
        /// <param name="signature">數字簽名</param>
        /// <returns></returns>
        public static T Post<T>(string url, string data, string userid, string signature)
        {
            return JsonConvert.DeserializeObject<T>(Post(url, data, userid, signature));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webApi"></param>
        /// <param name="queryStr"></param>
        /// <param name="userid"></param>
        /// <param name="signature"></param>
        /// <returns></returns>
        public static string Get(string webApi, string queryStr, string userid, string signature)
        {
            return GetData(webApi, queryStr, userid, signature);
        }

        /// <summary>
        /// Get請求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="webApi"></param>
        /// <param name="query"></param>
        /// <param name="queryStr"></param>
        /// <param name="userid"></param>
        /// <param name="signature"></param>
        /// <returns></returns>
        public static T Get<T>(string webApi, string queryStr, string userid, string signature)
        {
            return JsonConvert.DeserializeObject<T>(GetData(webApi, queryStr, userid, signature));
        }

        /// <summary>  
        /// 獲取時間戳  
        /// </summary>  
        /// <returns></returns>  
        private static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return ts.TotalSeconds.ToString();
        }


        /// <summary>  
        /// 獲取隨機數
        /// </summary>  
        /// <returns></returns>  
        private static string GetRandom()
        {
            Random rd = new Random(DateTime.Now.Millisecond);
            int i = rd.Next(0, int.MaxValue);
            return i.ToString();
        }
        /// <summary>
        /// Post請求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="data"></param>
        /// <param name="userid">用戶名稱</param>
        /// <param name="signature">數字簽名</param>
        /// <returns></returns>
        private static string PostData(string url, string data, string userid, string signature)
        {
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(data);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

                string timeStamp = GetTimeStamp();
                string nonce = GetRandom();
                //加入頭信息
                //當前請求用戶
                request.Headers.Add("userid", userid);
                //發起請求時的時間戳(單位:秒)
                request.Headers.Add("timestamp", timeStamp);
                //發起請求時的時間戳(單位:秒)
                request.Headers.Add("nonce", nonce);
                //當前請求內容的數字簽名
                request.Headers.Add("signature", signature);

                //寫數據
                request.Method = "POST";
                request.ContentLength = bytes.Length;
                request.ContentType = "application/json";
                request.GetRequestStream().Write(bytes, 0, bytes.Length);
                //讀數據
                request.Timeout = 300000;
                request.Headers.Set("Pragma", "no-cache");
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream streamReceive = response.GetResponseStream();
                StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8);
                string strResult = streamReader.ReadToEnd();
                
                //關閉流
                //reqstream.Close();
                streamReader.Close();
                streamReceive.Close();
                request.Abort();
                response.Close();
                return strResult;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// Get請求
        /// </summary>
        /// <param name="webApi"></param>
        /// <param name="queryStr"></param>
        /// <param name="userid"></param>
        /// <param name="signature"></param>
        /// <returns></returns>
        private static string GetData(string webApi, string queryStr, string userid, string signature)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webApi + "?" + queryStr);
                string timeStamp = GetTimeStamp();
                string nonce = GetRandom();
                //加入頭信息
                //當前請求用戶
                request.Headers.Add("userid", userid);
                //發起請求時的時間戳(單位:秒)
                request.Headers.Add("timestamp", timeStamp);
                //發起請求時的時間戳(單位:秒)
                request.Headers.Add("nonce", nonce);
                //當前請求內容的數字簽名
                request.Headers.Add("signature", signature);

                request.Method = "GET";
                request.ContentType = "application/json";
                request.Timeout = 90000;
                request.Headers.Set("Pragma", "no-cache");
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream streamReceive = response.GetResponseStream();
                StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8);
                string strResult = streamReader.ReadToEnd();

                streamReader.Close();
                streamReceive.Close();
                request.Abort();
                response.Close();
                return strResult;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

    }
}

Program.cs

using System;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
namespace SSLWebApiClient
{
    class Program
    {
        static void Main(string[] args)
        {
            string basicUrl = "http://zhyongfeng.com";
            string html = string.Empty;
            for (int i = 0; i < 1; i++)
            {
                //https協議基本認證 Authorization
                string url = basicUrl + "/api/base/Login?userId=zhyongfeng";
                ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                NetworkCredential credential = new NetworkCredential("zhyongfeng", "123456");
                req.Credentials = credential;
                HttpWebResponse response = (HttpWebResponse)req.GetResponse();
                Stream responseStream = response.GetResponseStream();
                StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);
                html = streamReader.ReadToEnd().Replace("\"", "");
                Console.WriteLine("Redis Token伺服器保存時間為12s");
                Console.WriteLine(String.Format("Redis伺服器返回的Token值為:{0}", html));
            }
            //token設置了12s有效期
            for (int j = 0; j < 5; j++)
            {
                System.Threading.Thread.Sleep(1000);
                string url = basicUrl + "/api/user/PostMessage";
                Console.WriteLine(Common.WebApiHelper.Post(url, JsonConvert.SerializeObject(new { msg = "hello" }), "zhyongfeng", html));
            }
            for (int j = 0; j < 10; j++)
            {
                System.Threading.Thread.Sleep(1000);
                string url = basicUrl + "/api/user/GetMachine";
                Console.WriteLine(Common.WebApiHelper.Get(url, null, "zhyongfeng", html));
            }
            Console.Read();
        }

    }
}

9       部署WebApi到本機

將WebApi部署到以下10.93.85.66(因網路限制,所以這裡只做單個集群,虛擬機ubuntu中的數據redis主要是NAT網路連接方式,使用埠轉發進行訪問)

10             Nginx集群配置搭建

通過自主義功能變數名稱zhyongfeng.com:80埠進行負載均衡集群訪問,則訪問C:\Windows\System32\drivers\etc\hosts,添加下列“本機IP 自定義的功能變數名稱”:

10.93.85.66     zhyongfeng.com

Nginx的集群配置:

worker_processes  1;

events {

    worker_connections  1024;

}

http {

    include       mime.types;

    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

 

    upstream zhyongfeng.com {

        server       10.93.85.66:20107;

    }

    server {

        listen       80;

        server_name  localhost;

        location / {

            proxy_pass   http://zhyongfeng.com;

        }

        error_page   500 502 503 504  /50x.html;

        location = /50x.html {

            root   html;

        }

    }
}

運行CMD:

D:\DTLDownLoads\nginx-1.10.2>start nginx

D:\DTLDownLoads\nginx-1.10.2>nginx -s reload

11             運行結果

訪問集群:http://zhyongfeng.com

WebApi經過Action過濾器,生成了Token值,並存儲到Redis,運行結果如下:

12             總結

Nginx集群使用Redis資料庫,客戶端利用 http basic身份驗證,訪問WebApi獲得Token並將Token存儲到Redis內在資料庫,通過Token值獲取相應的許可權數據,這樣子可以做到單點登錄,集群分散式的身份驗證效果。既方便了用戶在整個業務領域的系統操作,同時可以為整個公司、集團等各個區域的系統進行統一有效的身份驗證管理。

 

源代碼下載:

http://download.csdn.net/download/ruby_matlab/10155491

 

PDF下載:

Nginx集群之基於Redis的WebApi身份驗證.pdf


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

-Advertisement-
Play Games
更多相關文章
  • 1、C#的值類型 有幾個特點: 存儲在棧里 基於值類型的變數直接包含值(值類型存儲實際值)。 將一個值類型變數賦給另一個值類型變數時,將複製包含的值。 這與引用類型變數的賦值不同,引用類型變數的賦值只複製對對象的引用,而不複製對象本身。 所有的值類型均隱式派生自 System.ValueType。 ...
  • 註意事項 上一篇已經說明,這次就不一一說了,直接來正文; word內容 相關代碼 方法1 1 static void Main(string[] args) 2 { 3 string wordPathStr = @"C:\Users\user\Desktop\新建文件夾 (2)\openxml讀取表 ...
  • 首先我們知道http是一種無狀態的請求,他的生命周期就是從客戶端瀏覽器發出請求開始,到得到響應結束。那麼MVC應用程式從發出請求到獲得響應,都做了些什麼呢? 本文我們會詳細討論MVC應用程式一個請求的生命周期,從一個控制項到另一個控制項是怎樣被處理的。我們還會詳細介紹一下整個請求的生命周期中,用到的相關 ...
  • Core 的出現對我我沒有很大的影響,當時在Core要發佈的時候聽到周圍的人再聊再談,我沒有去太多關註,就是一個屌絲開發人員。 直到又一次偶然見到一位特別喜歡.net的老開發人員談起Core時落淚了,他是那樣的期待。 我開始關註Core並嘗試著去學習。 今天開始把我學到的一點一點的記錄下來。既希望給 ...
  • 獲取資料庫表名、表結構 public static DataTable GetSqlTables(string filterSql) { StringBuilder sb = new StringBuilder(); sb.Append(" select name from sysobjects w ...
  • NPOI 官網下載DLL:http://npoi.codeplex.com/releases 1、讀取Excel轉為DataTable ...
  • 介面是C#的一種引用數據類型。介面像是一個抽象類,可以定義方法成員,屬性,索引器和事件等,但是介面不提供對成員的實現,繼承介面的類必須提供介面成員的實現。 類用於描述的是事物的共性基本功能,介面用於定義的都是事物的額外功能。 一 介面的好處 規範性:定義介面像是在定義一種規範,當一個項目龐大複雜的時 ...
  • C# 語言經過專門設計,以便不同庫中的基類與派生類之間的版本控制可以不斷向前發展,同時保持後向相容。 這具有多方面的意義。例如,這意味著在基類中引入與派生類中的某個成員具有相同名稱的新成員在 C# 中是完全支持的,不會導致意外行為。 它還意味著類必須顯式聲明某方法是要替代一個繼承方法,還是本身就是一 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...