Skip to content

Commit ab6431b

Browse files
authored
doc(ISocketDataPropertyConverter): add ISocketDataPropertyConverter documentation (#6425)
* refactor: 增加数据保护 * doc: 增加通讯数据转实体类示例文档 * doc: 增加实体类文档
1 parent fdbe935 commit ab6431b

23 files changed

+272
-73
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
@page "/socket/data-entity"
2+
@inject IStringLocalizer<DataEntities> Localizer
3+
4+
<h3>@Localizer["DataEntityTitle"]</h3>
5+
<h4>@Localizer["DataEntityDescription"]</h4>
6+
7+
<DemoBlock Title="@Localizer["NormalTitle"]"
8+
Introduction="@Localizer["NormalIntro"]"
9+
Name="Normal" ShowCode="false">
10+
<p>通过 <code>SocketDataConverterAttribute</code> 类标签与 <code>SocketDataPropertyAttribute</code>
11+
属性标签可以将通讯数据自动转化为我们系统中需要的业务实体类,示例如下:</p>
12+
<Pre>[SocketDataConverter(Type = typeof(MockEntitySocketDataConverter))]
13+
class MockEntity
14+
{
15+
[SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
16+
public byte[]? Header { get; set; }
17+
18+
[SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
19+
public byte[]? Body { get; set; }
20+
21+
[SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
22+
public string? Value1 { get; set; }
23+
24+
[SocketDataProperty(Type = typeof(int), Offset = 8, Length = 1)]
25+
public int Value2 { get; set; }
26+
}</Pre>
27+
<p class="code-label">1. <code>SocketDataConverter</code> 参数说明:</p>
28+
<ul class="ul-demo">
29+
<li><b>Type</b>: 自定义转换器类型,组件库内置了 <code>SocketDataConverterBase</code> 泛型基类,继承后非常方便扩展出自己的转换器,重载
30+
<code>TryConvertTo</code> 方法调用基类的 <code>Parse</code> 基本就完成了 99% 的工作量,也可以完全自己解析通讯电文到实体类的各个属性上
31+
</li>
32+
</ul>
33+
<Pre>class MockEntitySocketDataConverter : SocketDataConverterBase&lt;MockEntity&gt;
34+
{
35+
public override bool TryConvertTo(ReadOnlyMemory&lt;byte&gt; data, [NotNullWhen(true)] out MockEntity? entity)
36+
{
37+
var v = new MockEntity();
38+
39+
// 调用基类的 Parse 方法即可
40+
var ret = Parse(data, v);
41+
entity = ret ? v : null;
42+
return ret;
43+
}
44+
}</Pre>
45+
<p><code>Parse</code> 方法也是可以重载自定义实现的,内置实现逻辑如下:</p>
46+
<Pre>protected virtual bool Parse(ReadOnlyMemory&lt;byte&gt; data, TEntity entity)
47+
{
48+
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
49+
var ret = false;
50+
if (entity != null)
51+
{
52+
var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
53+
foreach (var p in properties)
54+
{
55+
var attr = p.GetCustomAttribute&lt;SocketDataPropertyAttribute&gt;(false);
56+
if (attr != null)
57+
{
58+
p.SetValue(entity, attr.ConvertTo(data));
59+
}
60+
}
61+
ret = true;
62+
}
63+
return ret;
64+
}</Pre>
65+
<p>即遍历所有有 <code>SocketDataPropertyAttribute</code> 标签的属性对其进行过自动赋值</p>
66+
<p class="code-label">2. <code>SocketDataPropertyAttribute</code> 参数说明</p>
67+
<ul class="ul-demo">
68+
<li><b>Type</b>: 转换目标数据类型</li>
69+
<li><b>Offset</b>: 数据偏移量,即在接收到的数据中起始位置</li>
70+
<li><b>Length</b>: 数据长度,即在接收到的数据中占的长度</li>
71+
<li><b>EncodingName</b>: 转成字符串时需要的编码名称,默认 null 使用 <code>utf-8</code> 编码</li>
72+
<li><b>ConverterType</b>: 自定义转化器类型,继承 <code>ISocketDataPropertyConverter</code> 接口</li>
73+
<li><b>ConverterParameters</b>: 自定义转化器类型构造函数所需的参数,默认 null</li>
74+
</ul>
75+
<p>组件库内置了大量数据类型转换器</p>
76+
<ul class="ul-demo">
77+
<li><code>SocketDataByteArrayConverter</code> 转成 byte[] 数组类型</li>
78+
<li><code>SocketDataStringConverter</code> 转成 string 字符串类型</li>
79+
<li><code>SocketDataEnumConverter</code> 转成 enum 枚举类型</li>
80+
<li><code>SocketDataBoolConverter</code> 转成 bool 布尔类型</li>
81+
<li><code>SocketDataInt16BigEndianConverter</code> 转成 short 整形大端读取</li>
82+
<li><code>SocketDataInt32BigEndianConverter</code> 转成 int 整形大端读取</li>
83+
<li><code>SocketDataInt64BigEndianConverter</code> 转成 long 长整形大端读取</li>
84+
<li><code>SocketDataSingleBigEndianConverter</code> 转成 float 单精度浮点数大端读取</li>
85+
<li><code>SocketDataDoubleBigEndianConverter</code> 转成 double 双精度浮点数大端读取</li>
86+
<li><code>SocketDataUInt16BigEndianConverter</code> 转成 ushort 无符号整形大端读取</li>
87+
<li><code>SocketDataUInt32BigEndianConverter</code> 转成 uint 无符号整形大端读取</li>
88+
<li><code>SocketDataUInt64BigEndianConverter</code> 转成 ulong 无符号长整形大端读取</li>
89+
<li><code>SocketDataInt16LittleEndianConverter</code> 转成 short 整形小端读取</li>
90+
<li><code>SocketDataInt32LittleEndianConverter</code> 转成 int 整形小端读取</li>
91+
<li><code>SocketDataInt64LittleEndianConverter</code> 转成 long 长整形小端读取</li>
92+
<li><code>SocketDataSingleLittleEndianConverter</code> 转成 float 单精度浮点数小端读取</li>
93+
<li><code>SocketDataDoubleLittleEndianConverter</code> 转成 double 双精度浮点数小端读取</li>
94+
<li><code>SocketDataUInt16LittleEndianConverter</code> 转成 ushort 无符号整形小端读取</li>
95+
<li><code>SocketDataUInt32LittleEndianConverter</code> 转成 uint 无符号整形小端读取</li>
96+
<li><code>SocketDataUInt64LittleEndianConverter</code> 转成 ulong 无符号整形小端读取</li>
97+
</ul>
98+
<p>自定义数据类型转化器示例</p>
99+
<Pre>[SocketDataConverter(Type = typeof(MockEntitySocketDataConverter))]
100+
class MockEntity
101+
{
102+
[SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
103+
public byte[]? Header { get; set; }
104+
105+
[SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
106+
public byte[]? Body { get; set; }
107+
108+
[SocketDataProperty(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
109+
public string? Value1 { get; set; }
110+
}
111+
112+
class FooConverter(string name) : ISocketDataPropertyConverter
113+
{
114+
public object? Convert(ReadOnlyMemory&lt;byte&gt; data)
115+
{
116+
return new Foo() { Id = data.Span[0], Name = name };
117+
}
118+
}</Pre>
119+
<p>更多技术细节可以参考以上内置提供的转换器源码</p>
120+
</DemoBlock>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Server.Components.Samples.Sockets;
7+
8+
/// <summary>
9+
/// 数据转实体类示例
10+
/// </summary>
11+
public partial class DataEntities
12+
{
13+
14+
}

src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,27 +209,29 @@ void AddSocket(DemoMenuItem item)
209209
{
210210
new()
211211
{
212-
IsNew = true,
213212
Text = Localizer["SocketManualReceive"],
214213
Url = "socket/manual-receive"
215214
},
216215
new()
217216
{
218-
IsNew = true,
219217
Text = Localizer["SocketAutoReceive"],
220218
Url = "socket/auto-receive"
221219
},
222220
new()
223221
{
224-
IsNew = true,
225222
Text = Localizer["DataPackageAdapter"],
226223
Url = "socket/adapter"
227224
},
228225
new()
229226
{
230-
IsNew = true,
231227
Text = Localizer["SocketAutoConnect"],
232228
Url = "socket/auto-connect"
229+
},
230+
new()
231+
{
232+
IsNew = true,
233+
Text = Localizer["SocketDataEntity"],
234+
Url = "socket/data-entity"
233235
}
234236
};
235237
AddBadge(item, count: 1);

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4841,6 +4841,7 @@
48414841
"SocketManualReceive": "Manual Receive",
48424842
"DataPackageAdapter": "DataPackageAdapter",
48434843
"SocketAutoConnect": "Reconnect",
4844+
"SocketDataEntity": "DataEntity",
48444845
"NetworkMonitor": "Network Monitor"
48454846
},
48464847
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
@@ -7121,6 +7122,12 @@
71217122
"NormalTitle": "Basic usage",
71227123
"NormalIntro": "Enable automatic reconnection by setting <code>IsAutoReconnect</code>"
71237124
},
7125+
"BootstrapBlazor.Server.Components.Samples.Sockets.DataEntities": {
7126+
"DataEntityTitle": "Socket Auto Entity",
7127+
"DataEntityDescription": "After receiving the communication data, it is automatically converted into the entity class required by the business",
7128+
"NormalTitle": "Basic usage",
7129+
"NormalIntro": "Enable automatic data conversion through the <code>SocketDataConverterAttribute</code> tag"
7130+
},
71247131
"BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
71257132
"NetworkMonitorTitle": "NetworkMonitor",
71267133
"NetworkMonitorDescription": "Use the browser native API <code>navigator.connection</code> to display the current network status in real time",

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4841,6 +4841,7 @@
48414841
"SocketManualReceive": "手动接收数据",
48424842
"DataPackageAdapter": "数据处理器",
48434843
"SocketAutoConnect": "自动重连",
4844+
"SocketDataEntity": "通讯数据转实体类",
48444845
"NetworkMonitor": "网络状态 NetworkMonitor"
48454846
},
48464847
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
@@ -7121,6 +7122,12 @@
71217122
"NormalTitle": "基本用法",
71227123
"NormalIntro": "通过设置 <code>IsAutoReconnect</code> 开启自动重连机制"
71237124
},
7125+
"BootstrapBlazor.Server.Components.Samples.Sockets.DataEntities": {
7126+
"DataEntityTitle": "Socket 数据转化实体类",
7127+
"DataEntityDescription": "接收到通讯数据后自动转成业务需要的实体类",
7128+
"NormalTitle": "基本用法",
7129+
"NormalIntro": "通过 <code>SocketDataConverterAttribute</code> 标签开启数据自动转换功能"
7130+
},
71247131
"BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
71257132
"NetworkMonitorTitle": "NetworkMonitor 网络状态",
71267133
"NetworkMonitorDescription": "使用浏览器原生 api <code>navigator.connection</code> 实时显示当前网络状态",

src/BootstrapBlazor.Server/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@
247247
"socket/auto-receive": "Sockets\\AutoReceives",
248248
"socket/adapter": "Sockets\\Adapters",
249249
"socket/auto-connect": "Sockets\\AutoReconnects",
250+
"socket/data-entity": "Sockets\\DataEntities",
250251
"network-monitor": "NetworkMonitors"
251252
},
252253
"video": {

src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public abstract class SocketDataConverterBase<TEntity> : ISocketDataConverter<TE
2828
/// <param name="entity"></param>
2929
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
3030
{
31-
// 使用 SocketDataFieldAttribute 特性获取数据转换规则
31+
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
3232
var ret = false;
3333
if (entity != null)
3434
{

src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ public class SocketDataDoubleBigEndianConverter : ISocketDataPropertyConverter
1919
public object? Convert(ReadOnlyMemory<byte> data)
2020
{
2121
double ret = 0;
22-
Span<byte> paddedSpan = stackalloc byte[8];
23-
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
24-
if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
22+
if (data.Length <= 8)
2523
{
26-
ret = v;
24+
Span<byte> paddedSpan = stackalloc byte[8];
25+
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
26+
if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
27+
{
28+
ret = v;
29+
}
2730
}
2831
return ret;
2932
}

src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ public class SocketDataDoubleLittleEndianConverter : ISocketDataPropertyConverte
1919
public object? Convert(ReadOnlyMemory<byte> data)
2020
{
2121
double ret = 0;
22-
Span<byte> paddedSpan = stackalloc byte[8];
23-
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
24-
if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
22+
if (data.Length <= 8)
2523
{
26-
ret = v;
24+
Span<byte> paddedSpan = stackalloc byte[8];
25+
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
26+
if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
27+
{
28+
ret = v;
29+
}
2730
}
2831
return ret;
2932
}

src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ public class SocketDataInt16BigEndianConverter : ISocketDataPropertyConverter
1919
public object? Convert(ReadOnlyMemory<byte> data)
2020
{
2121
short ret = 0;
22-
Span<byte> paddedSpan = stackalloc byte[2];
23-
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
24-
if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
22+
if (data.Length <= 2)
2523
{
26-
ret = v;
24+
Span<byte> paddedSpan = stackalloc byte[2];
25+
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
26+
if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
27+
{
28+
ret = v;
29+
}
2730
}
2831
return ret;
2932
}

0 commit comments

Comments
 (0)