bh003- Blazor hybrid / Maui 使用藍牙BLE快速教程

来源:https://www.cnblogs.com/densen2014/archive/2023/08/23/17649961.html
-Advertisement-
Play Games

### 1. 建立工程 bh003_ble [源碼](https://github.com/densen2014/BlazorHybrid/tree/master/bh100days/bh003_ble?WT.mc_id=DT-MVP-5005078) ### 2. 添加 nuget 包 ``` ` ...


1. 建立工程 bh003_ble

源碼

2. 添加 nuget 包

<PackageReference Include="BlazorHybrid.Maui.Permissions" Version="0.0.2" />
<PackageReference Include="BootstrapBlazor" Version="7.*" />
<PackageReference Include="Densen.Extensions.BootstrapBlazor" Version="7.*" />

BlazorHybrid.Maui.Permissions 因為源碼比較長,主要是一些檢查和申請許可權,BLE許可權相關代碼,就不占用篇幅列出,感興趣的同學直接打開源碼參考

順便打開可空 <Nullable>enable</Nullable>

3. 添加藍牙許可權

安卓

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <!--藍牙-->
  <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

  <!-- csproj文件指定SupportedOSPlatformVersion android 28.0 可以繼續使用安卓9的許可權 -->
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.BLUETOOTH"/>
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

  <!-- csproj文件指定SupportedOSPlatformVersion android 31.0 使用安卓12的許可權 -->
  <!-- Android 12以下才需要定位許可權,Android 9以下官方建議申請ACCESS_COARSE_LOCATION -->

  <!--<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30"/>
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
	<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
	<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>-->

  <!-- Android 12在不申請定位許可權時,必須加上android:usesPermissionFlags="neverForLocation",否則搜不到設備 -->
  <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
  <!--藍牙 END-->
</manifest>

iOS

Info.plist

    <key>UIBackgroundModes</key>
    <array>
        <string>bluetooth-central</string>
        <string>bluetooth-peripheral</string>
    </array>
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>此應用程式需要訪問您的藍牙。請根據要求授予許可權.</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>此應用程式需要訪問您的藍牙。請根據要求授予許可權.</string>

以下是完整文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>UIDeviceFamily</key>
    <array>
        <integer>1</integer>
        <integer>2</integer>
    </array>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>arm64</string>
    </array>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>XSAppIconAssets</key>
    <string>Assets.xcassets/appicon.appiconset</string>
    <key>UIBackgroundModes</key>
    <array>
        <string>bluetooth-central</string>
        <string>bluetooth-peripheral</string>
    </array>
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>此應用程式需要訪問您的藍牙。請根據要求授予許可權.</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>此應用程式需要訪問您的藍牙。請根據要求授予許可權.</string>

</dict>
</plist>

Windows

Package.appxmanifest

4. 編輯 Index.html 文件,引用 BootstrapBlazor UI 庫.

完整文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
    <title>bh003_ble</title>
    <base href="/" />
    <link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet">
    <link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet">
    <link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet">
    <link href="css/app.css" rel="stylesheet" />
    <link href="bh003_ble.styles.css" rel="stylesheet" />
</head>

<body>

    <div class="status-bar-safe-area"></div>

    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss"><i class="fa-solid fa-xmark"></i></a>
    </div>

    <script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
    <script src="_framework/blazor.webview.js" autostart="false"></script>

</body>

</html>

5. 添加 BootstrapBlazorRoot 組件

Main.razor 文件添加 BootstrapBlazorRoot 組件

6. 添加命名空間引用

_Imports.razor

@using BootstrapBlazor.Components

7. 添加服務

MauiProgram.cs

添加

            builder.Services.AddDensenExtensions();
            builder.Services.ConfigureJsonLocalizationOptions(op =>
            {
                // 忽略文化信息丟失日誌
                op.IgnoreLocalizerMissing = true;

            });
            builder.Services.AddSingleton<BluetoothLEServices>();
            builder.Services.AddScoped<IStorage, StorageService>();

完整文件

using bh003_ble.Data;
using Microsoft.Extensions.Logging;
using BlazorHybrid.Maui.Shared;
using BootstrapBlazor.WebAPI.Services;

namespace bh003_ble
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                });

            builder.Services.AddMauiBlazorWebView();

#if DEBUG
		builder.Services.AddBlazorWebViewDeveloperTools();
		builder.Logging.AddDebug();
#endif

            builder.Services.AddSingleton<WeatherForecastService>();
            builder.Services.AddDensenExtensions();
            builder.Services.ConfigureJsonLocalizationOptions(op =>
            {
                // 忽略文化信息丟失日誌
                op.IgnoreLocalizerMissing = true;

            });
            builder.Services.AddSingleton<BluetoothLEServices>();
            builder.Services.AddScoped<IStorage, StorageService>();

            return builder.Build();
        }
    }
}

8. 添加代碼後置文件 Pages/Index.razor.cs

Index.razor.cs

using BlazorHybrid.Core.Device;
using BlazorHybrid.Maui.Shared;
using BootstrapBlazor.Components;
using BootstrapBlazor.WebAPI.Services;
using Microsoft.AspNetCore.Components;
using System.Diagnostics.CodeAnalysis;

namespace bh003_ble.Pages;

public partial class Index : IAsyncDisposable
{
    [Inject, NotNull]
    BluetoothLEServices? MyBleTester { get; set; }
    [Inject, NotNull] protected IStorage? Storage { get; set; }
    [Inject, NotNull] protected ToastService? ToastService { get; set; }

    public void SetTagDeviceName(BleTagDevice ble)
    {
        MyBleTester.TagDevice = ble;

        if (!isInit)
        {
            MyBleTester.OnMessage += OnMessage;
            MyBleTester.OnDataReceived += OnDataReceived;
            MyBleTester.OnStateConnect += OnStateConnect;
            isInit = true;
        }
    }

    public event Action<string>? OnMessage;
    public event Action<string>? OnDataReceived;
    public event Action<bool>? OnStateConnect;

    bool isInit = false;

    public async Task<List<BleDevice>?> StartScanAsync() => await MyBleTester.StartScanAsync();

    public async Task<List<BleService>?> ConnectToKnownDeviceAsync(Guid deviceID, string? deviceName = null) => await MyBleTester.ConnectToKnownDeviceAsync(deviceID, deviceName);

    public async Task<List<BleCharacteristic>?> GetCharacteristicsAsync(Guid serviceid) => await MyBleTester.GetCharacteristicsAsync(serviceid);

    public async Task<string?> ReadDeviceName(Guid? serviceid, Guid? characteristic) => await MyBleTester.ReadDeviceName(serviceid, characteristic);

    public async Task<byte[]?> ReadDataAsync(Guid characteristic) => await MyBleTester.ReadDataAsync(characteristic);

    public async Task<bool> SendDataAsync(Guid characteristic, byte[] ary) => await MyBleTester.SendDataAsync(characteristic, ary);

    public async Task<bool> DisConnectDeviceAsync() => await MyBleTester.DisConnectDeviceAsync();


    public Task<bool> BluetoothIsBusy() => MyBleTester.BluetoothIsBusy();




    private bool IsScanning = false;
    private List<BleDevice>? Devices { get; set; }
    private List<BleService>? Services { get; set; }
    private List<BleCharacteristic>? Characteristics { get; set; }
    private string? ReadResult { get; set; }
    private string? Message { get; set; } = "";

    BleTagDevice BleInfo { get; set; } = new BleTagDevice();

    private List<SelectedItem> DemoList { get; set; } = new List<SelectedItem>() { new SelectedItem() { Text = "測試數據", Value = "" } };
    private List<SelectedItem> DeviceList { get; set; } = new List<SelectedItem>();
    private List<SelectedItem> ServiceidList { get; set; } = new List<SelectedItem>();
    private List<SelectedItem> CharacteristicList { get; set; } = new List<SelectedItem>();

    private Dictionary<string, object>? IsScanningCss => IsScanning ? new() { { "disabled", "" }, } : null;

    bool IsAutoConnect { get; set; }
    bool IsAuto { get; set; }
    bool IsInit { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await Init();
        }
    }

    async Task<bool> Init()
    {
        try
        {

            if (IsInit) return true;

            if (await BluetoothIsBusy())
            {
                await ToastService.Warning("藍牙正在使用中,請稍後再試");
                return false;
            }
            OnMessage += Tools_OnMessage;
            OnDataReceived += Tools_OnDataReceived;
            OnStateConnect += Tools_OnStateConnect;
            SetTagDeviceName(BleInfo);
            IsInit = true;

            StateHasChanged();

            var deviceID = await Storage.GetValue("bleDeviceID", string.Empty);
            if (!string.IsNullOrEmpty(deviceID))
            {
                BleInfo.Name = await Storage.GetValue("bleDeviceName", string.Empty);
                BleInfo.DeviceID = Guid.Parse(deviceID);
                var serviceid = await Storage.GetValue("bleServiceid", string.Empty);
                if (!string.IsNullOrEmpty(serviceid)) BleInfo.Serviceid = Guid.Parse(serviceid);
                var characteristic = await Storage.GetValue("bleCharacteristic", string.Empty);
                if (!string.IsNullOrEmpty(characteristic)) BleInfo.Characteristic = Guid.Parse(characteristic);
                var auto = await Storage.GetValue("bleAutoConnect", string.Empty);
                if (auto == "True")
                {
                    IsAuto = true;
                    await AutoRead();

                }
            }
            return true;

        }
        catch (Exception ex)
        {
            System.Console.WriteLine(ex.Message);
        }
        return false;
    }


    private async Task AutoRead()
    {
        Services = null;
        Characteristics = null;
        Message = "";
        ReadResult = "";
        Devices = new List<BleDevice>() { new BleDevice() { Id = BleInfo.DeviceID, Name = BleInfo.Name } };
        DeviceList = new List<SelectedItem>() { new SelectedItem() { Text = BleInfo.Name, Value = BleInfo.DeviceID.ToString() } };
        IsAutoConnect = true;
        await OnDeviceSelect();
        IsAutoConnect = false;
    }

    private async Task OnStateChanged(bool value)
    {
        await Storage.SetValue("bleAutoConnect", value.ToString());
    }

    private void Tools_OnStateConnect(bool obj)
    {

    }

    private async void Tools_OnDataReceived(string message)
    {
        ReadResult = message;
        Tools_OnMessage(message);
        await InvokeAsync(StateHasChanged);
    }

    private async void Tools_OnMessage(string message)
    {
        if (Message != null && Message.Length > 500) Message = Message.Substring(0, 500);
        Message = $"{message}\r\n{Message}";
        await InvokeAsync(StateHasChanged);
    }


    //掃描外設
    private async void ScanDevice()
    {
        if (!await Init()) return;

        IsScanning = true;
        Devices = null;
        Services = null;
        Characteristics = null;
        Message = "";
        ReadResult = "";
        DeviceList = new List<SelectedItem>() { new SelectedItem() { Text = "請選擇...", Value = "" } };

        //開始掃描
        Devices = await StartScanAsync();

        if (Devices != null)
        {
            Devices.ForEach(a => DeviceList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.DeviceID, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
        }

        IsScanning = false;

        //非同步更新UI
        await InvokeAsync(StateHasChanged);
    }

    //連接外設
    private async Task OnDeviceSelect(SelectedItem item)
    {
        if (IsAutoConnect || item.Value == "") return;
        BleInfo.Name = item.Text;
        BleInfo.DeviceID = Guid.Parse(item.Value);
        await OnDeviceSelect();
    }

    private async Task OnDisConnectDevice()
    {
        if (await DisConnectDeviceAsync())
            await ToastService.Success("斷開成功");
        else
            await ToastService.Error("斷開失敗");
    }

    private async Task OnDeviceSelect()
    {

        Services = null;
        Characteristics = null;
        Message = "";
        ReadResult = "";
        ServiceidList = new List<SelectedItem>() { new SelectedItem() { Text = "請選擇...", Value = "" } };
        //連接外設
        Services = await ConnectToKnownDeviceAsync(BleInfo.DeviceID, BleInfo.Name);
        if (Services != null)
        {
            Services.ForEach(a => ServiceidList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.Serviceid, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
            await Storage.SetValue("bleDeviceID", BleInfo.DeviceID.ToString());
            await Storage.SetValue("bleDeviceName", BleInfo.Name ?? "上次設備");
            if (BleInfo.Serviceid != Guid.Empty && IsAutoConnect)
            {
                await OnServiceidSelect();
            }
        }

        //非同步更新UI
        await InvokeAsync(StateHasChanged);
    }


    private async Task OnServiceidSelect(SelectedItem item)
    {
        if (IsAutoConnect || item.Value == "") return;
        BleInfo.Serviceid = Guid.Parse(item.Value);
        await OnServiceidSelect();
    }
    private async Task OnServiceidSelect()
    {
        Characteristics = null;
        Message = "";
        ReadResult = "";
        CharacteristicList = new List<SelectedItem>() { new SelectedItem() { Text = "請選擇...", Value = "" } };
        Characteristics = await GetCharacteristicsAsync(BleInfo.Serviceid);
        if (Characteristics != null)
        {
            Characteristics.ForEach(a => CharacteristicList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.Characteristic, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
            await Storage.SetValue("bleServiceid", BleInfo.Serviceid.ToString());
            if (BleInfo.Characteristic != Guid.Empty && IsAutoConnect)
            {
                await ReadDeviceName();
            }
        }
        await InvokeAsync(StateHasChanged);
    }

    private async Task OnCharacteristSelect(SelectedItem item)
    {
        if (IsAutoConnect) return;
        BleInfo.Characteristic = Guid.Parse(item.Value);
        await ReadDeviceName();
    }

    //讀取數值
    private async Task ReadDeviceName()
    {
        Message = "";

        //讀取數值
        ReadResult = await ReadDeviceName(BleInfo.Serviceid, BleInfo.Characteristic);
        await Storage.SetValue("bleCharacteristic", BleInfo.Characteristic.ToString());

        if (!string.IsNullOrEmpty(ReadResult)) await ToastService.Information("讀取成功", ReadResult);
        //非同步更新UI
        await InvokeAsync(StateHasChanged);
    }

    private async Task ReadDataAsync()
    {
        Message = "";
        //讀取數值
        var res = await ReadDataAsync(BleInfo.Characteristic);
        if (!string.IsNullOrEmpty(ReadResult)) await ToastService.Information("讀取成功", res.ToString());

        //非同步更新UI
        await InvokeAsync(StateHasChanged);
    }

    private async Task SendDataAsync()
    {
        Message = "";
        //讀取數值
        var res = await SendDataAsync(BleInfo.Characteristic, null);
        await ToastService.Information("成功發送", res.ToString());

        //非同步更新UI
        await InvokeAsync(StateHasChanged);
    }

    ValueTask IAsyncDisposable.DisposeAsync()
    {
        OnMessage -= Tools_OnMessage;
        OnDataReceived -= Tools_OnDataReceived;
        OnStateConnect -= Tools_OnStateConnect;
        return new ValueTask();
    }

}


9. 添加 UI Pages/Index.razor

Index.razor

@page "/"

<h3>藍牙</h3>

<div class="row g-3">
    <div class="btn-group" role="group">
        <Button Text="掃描外設" @attributes=IsScanningCss OnClick=ScanDevice />
        @if (Devices != null)
        {
            <Button Text="連接" OnClick="OnDeviceSelect" />
            <Button Text="斷開" OnClick="OnDisConnectDevice" />
            @if (Characteristics != null)
            {
                <Button Text="寫入" OnClick="ReadDeviceName" />
                <Button Text="讀取" OnClick="ReadDeviceName" />
            }
        }
    </div>
</div>
@if (Devices != null)
{

    <div class="row g-3">
        <div class="col-12 col-sm-3">
            <Select TValue="Guid" Items="DeviceList" OnSelectedItemChanged="OnDeviceSelect" />
        </div>
        @if (Services != null)
        {
            <div class="col-12 col-sm-3">
                <Select TValue="Guid" Items="ServiceidList" OnSelectedItemChanged="OnServiceidSelect" />
            </div>
            @if (Characteristics != null)
            {
                <div class="col-12 col-sm-3">
                    <Select TValue="Guid" Items="CharacteristicList" OnSelectedItemChanged="OnCharacteristSelect" />
                </div>
                @if (ReadResult != null)
                {
                    <div class="col-12 col-sm-3">
                        <Display TValue="string" Value="@ReadResult" />
                    </div>
                }
            }
        }
    </div>

}

@if (BleInfo.Name != null)
{

    <div class="g-3">
        歷史連接 <br />
        @BleInfo.Name <br />
        @BleInfo.DeviceID <br />
        @BleInfo.Serviceid <br />
        @BleInfo.Characteristic <br />
        @ReadResult <br />
    </div>

}
<Switch DisplayText="自動連接" OnText="自動連接" OffText="手動連接" Value="@IsAuto" OnValueChanged="@OnStateChanged" />

<pre style="max-height: 500px; overflow-y: scroll; white-space: pre-wrap; word-wrap: break-word;">@Message</pre>

10. 運行

11. 相關資料

如何遠程調試 MAUI blazor / Blazor Hybrid
https://www.cnblogs.com/densen2014/p/16988516.html

關聯項目

FreeSql QQ群:4336577

BA & Blazor QQ群:795206915

Maui Blazor 中文社區 QQ群:645660665

知識共用許可協議

本作品採用 知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名AlexChow(包含鏈接: https://github.com/densen2014 ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫

轉載聲明

本文來自博客園,作者:周創琳 AlexChow,轉載請註明原文鏈接:https://www.cnblogs.com/densen2014/p/17649961.html

AlexChow

今日頭條 | 博客園 | 知乎 | Gitee | GitHub

image


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

-Advertisement-
Play Games
更多相關文章
  • ![](https://img2023.cnblogs.com/other/1218593/202308/1218593-20230822164212978-1679813836.png) ### 背景 有時候我們需要進行遠程的debug,本文研究如何進行遠程debug,以及使用 IDEA 遠程de ...
  • ## 1 概要 通過引入結構化併發編程的API,簡化併發編程。結構化併發將在不同線程中運行的相關任務組視為單個工作單元,從而簡化錯誤處理和取消操作,提高可靠性,並增強可觀察性。這是一個預覽版的API。 ## 2 歷史 結構化併發是由JEP 428提出的,併在JDK 19中作為孵化API發佈。它在JD ...
  • [TOC] ## 一、mall開源項目 ### 1.1 來源 **mall學習教程**,架構、業務、技術要點全方位解析。mall項目(**50k+star**)是一套電商系統,使用現階段主流技術實現。涵蓋了SpringBoot 2.3.0、MyBatis 3.4.6、Elasticsearch 7. ...
  • 函數是任何一門高級語言中必須要存在的,使用函數式編程可以讓程式可讀性更高,充分發揮了模塊化設計思想的精髓,今天我將帶大家一起來探索函數的實現機理,探索編譯器到底是如何對函數這個關鍵字進行實現的,並使用彙編語言模擬實現函數編程中的參數傳遞調用規範等。說到函數我們必須要提起調用約定這個名詞,而調用約定離... ...
  • 本文是區塊鏈瀏覽器系列的第四篇。 在[上一篇文章](https://mengbin.top/2023-08-13-blockBrowser/)介紹如何解析區塊數據時,使用`session`對客戶端上傳的pb文件進行區分,到期後自動刪除。 在這片文章中,會著重介紹下認證系統的實現,主要分為三部分: - ...
  • 需求背景 需要在前端頁面展示當前表欄位的所有上下游血緣關係,以進一步做數據診斷治理。大致效果圖如下: 首先這裡解釋什麼是表欄位血緣關係,SQL 示例: CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status FROM t ...
  • 由於本人太弱,可能講解有誤,請讀者指出。 # 什麼是網路流 網路流是通過構建從源點到匯點的有向圖模型來解決圖論問題。從理論上講,網路流可以處理所有二分圖問題。 二分圖和網路流的難度都在於問題建模,一般不會特意去卡演算法效率,所以只需要背一兩個簡單演算法的模板就能應付大部分題目了。 # 最大流問題 ## ...
  • 1.已解密的登錄請求 推理: AppScan 識別了不是通過 SSL 發送的登錄請求。 測試請求和響應: 1.1.1 產生的原因 登錄介面,前端傳入的密碼參數沒有經過md5的加密就直接傳給了後端 1.1.2 解決方法 前端代碼傳參的時候做md5加密處理 2.會話標識未更新 推理: 測試結果似乎指示存 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...