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

news2025/1/16 4:01:36

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/914789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习笔记之优化算法(十九)经典牛顿法的收敛性分析

机器学习笔记之优化算法——经典牛顿法的收敛性分析 引言回顾&#xff1a;算法的收敛性分析 Wolfe \text{Wolfe} Wolfe准则的收敛性分析梯度下降法在凸函数的收敛性分析梯度下降法在强凸函数的收敛性分析 经典牛顿法的收敛性分析收敛性定理介绍证明过程关于隐含条件的说明 引言…

Spring之域对象共享数据

文章目录 前言一、requset域1.使用ServletAPI向request域对象共享数据2.使用ModelAndView向request域对象共享数据3.使用Model向request域对象共享数据4.使用map向request域对象共享数据5.使用ModelMap向request域对象共享数据6.Model、ModelMap、Map的关系 二、session域向ses…

语谱图(一) Spectrogram 的定义与机理

1. 语谱图 spectrogram 在音频、语音信号处理领域&#xff0c;我们需要将信号转换成对应的语谱图(spectrogram)&#xff0c;将语谱图上的数据作为信号的特征。 语谱图的横坐标是时间&#xff0c;纵坐标是频率&#xff0c;坐标点值为语音数据能量。由于是采用二维平面表达三维…

UE4 材质学习笔记

CheapContrast与CheapContrast_RGB都是提升对比度的&#xff0c;一个是一维输入&#xff0c;一个是三维输入&#xff0c;让亮的地方更亮&#xff0c;暗的地方更暗&#xff0c;不像power虽然也是提升对比度&#xff0c;但是使用过后的结果都是变暗或者最多不变&#xff08;值为1…

国标视频云服务平台EasyGBS国标平台内网访问正常但公网无法访问的问题解决方案

国标视频云服务平台EasyGBS可支持通过国标GB28181协议&#xff0c;接入多路视频源设备&#xff0c;实现视频流的接入、转码、处理与分发等功能&#xff0c;对外输出的视频流格式包括RTSP、RTMP、FLV、HLS、WebRTC等。平台视频能力丰富灵活&#xff0c;包括监控直播、视频分发、…

Maven 配置文件修改及导入第三方jar包

设置java和maven的环境变量 修改maven配置文件 &#xff08;D:\app\apache-maven-3.5.0\conf\settings.xml&#xff0c;1中环境变量对应的maven包下的conf&#xff09; 修改131行左右的mirror&#xff0c;设置阿里云的仓库地址 <mirror> <id>alimaven</id&g…

如何选择合适的量化交易服务器

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

sql入门-多表查询

案例涉及表 ----------------------------------建表语句之前翻看之前博客文章 多表查询 -- 学生表 create table studen ( id int primary key auto_increment comment id, name varchar(50) comment 姓名, no varchar(10) comment 学号 ) comment 学生表; insert…

ES基础操作

1.创建索引 在 Postman 中&#xff0c;向 ES 服务器发 PUT 请求 &#xff1a; http://127.0.0.1:9200/shopping 后台日志 重复发送 PUT 请求添加索引 &#xff1a; http://127.0.0.1:9200/shopping &#xff0c;会返回错误信息 : 2.获取单个索引相关信息 在 Postman 中&#…

【SpringSecurity】三、访问授权

文章目录 1、配置用户权限2、针对URL授权3、针对方法的授权 1、配置用户权限 继续上一章&#xff0c;给在内存中创建两个用户配置权限。配置权限有两种方式&#xff1a; 配置roles配置authorities //哪个写在后面哪个起作用 //角色变成权限后会加一个ROLE_前缀&#xff0c;比…

Flask狼书笔记 | 03_模板

文章目录 3 模板3.1 模板基本使用3.2 模板结构组织3.3 模板进阶 3 模板 模板&#xff08;template&#xff09;&#xff1a;包含固定内容和动态部分的可重用文件。Jinja2模板引擎可用于任何纯文本文件。 3.1 模板基本使用 HTML实体&#xff1a;https://dev.w3.org/html5/htm…

启动Vue项目踩坑记录

前言 在启动自己的Vue项目时&#xff0c;遇到一些报错&#xff0c;当时很懵&#xff0c;解决了以后豁然开朗&#xff0c;特写此博客记录一下。 一、<template>里多加了个div标签 [vite] Internal server error: At least one <template> or <script> is req…

EureKa快速入门

EureKa快速入门 远程调用的问题 多个服务有多个端口&#xff0c;这样的话服务有多个&#xff0c;硬编码不太适合 eureKa的作用 将service的所有服务的端口全部记录下来 想要的话 直接从注册中心查询对于所有服务 每隔一段时间需要想eureKa发送请求 保证服务还存活 动手实践 …

odoo安装启动遇到的问题

问题&#xff1a;在第一次加载odoo配置文件的时候&#xff0c;启动失败 方法&#xff1a; 1、先检查odoo.conf的内容&#xff0c;尤其是路径 [options] ; This is the password that allows database operations: ; admin_passwd admin db_host 127.0.0.1 db_port 5432 d…

kotlin协程flow任务意外结束未emit数据retryWhen onEmpty(5)

kotlin协程flow任务意外结束未emit数据retryWhen onEmpty&#xff08;5&#xff09; import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeoutOrNullfun main(args: Array<String&…

【Java】基础练习(九)

1.结婚 创建一个Person类&#xff0c;如下: public class Person{private String name;private Character gender;private Integer age;private Boolean marry;// 省略 getter / settter / 构造 / toString / hashCode / equals }有一个类CAB&#xff0c;有一个canMarry方…

SpringBoot(二)

###SpringBoot原理分析 ###SpringBoot监控 ###SpringBoot项目部署 #SpringBoot自动配置 Condition&#xff1a;&#xff08;条件&#xff09; Condition是在Spring4.0增加的条件判断功能&#xff0c;通过这个功能可以实现选择性的创建Bean操作 SpringBoot是如何知道要创建…

如何在服务器上用kaggle下载数据集

S1 服务器上安装kaggle cli工具 pip install --user kaggleS2 服务器上创建kaggle目录 mkdir ~/.kaggleS3 进入kaggle账户创建token 生成token 点击右上角头像&#xff0c;选择setting 点击create new token 进入你的浏览器下载页&#xff0c;可以看到有了一个kaggle.jso…

快速了解什么是Cookie

&#x1f600;前言 本篇博文是关于Web 开发会话技术 -Cookie的介绍&#xff0c;希望你能够喜欢&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的…

Elasticsearch 入门安装

1.Elasticsearch 是什么 The Elastic Stack, 包括 Elasticsearch、 Kibana、 Beats 和 Logstash&#xff08;也称为 ELK Stack&#xff09;。能够安全可靠地获取任何来源、任何格式的数据&#xff0c;然后实时地对数据进行搜索、分析和可视化。 Elaticsearch&#xff0c;简称为…