-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
抽象提取BaseDeviceController,作为设备控制器基类,提供登录注销心跳等接口
- Loading branch information
Showing
11 changed files
with
277 additions
and
112 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
158 changes: 158 additions & 0 deletions
158
NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
using System.Net; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using NewLife.Http; | ||
using NewLife.Log; | ||
using NewLife.Remoting.Extensions.Services; | ||
using NewLife.Remoting.Models; | ||
using WebSocket = System.Net.WebSockets.WebSocket; | ||
|
||
namespace NewLife.Remoting.Extensions; | ||
|
||
/// <summary>设备控制器</summary> | ||
[ApiFilter] | ||
[ApiController] | ||
[Route("[controller]")] | ||
public class BaseDeviceController : BaseController | ||
{ | ||
/// <summary>设备</summary> | ||
protected IDevice _device = null!; | ||
|
||
private readonly IDeviceService _deviceService; | ||
private readonly ITracer? _tracer; | ||
|
||
#region 构造 | ||
/// <summary>实例化设备控制器</summary> | ||
/// <param name="serviceProvider"></param> | ||
public BaseDeviceController(IServiceProvider serviceProvider) : base(serviceProvider) | ||
{ | ||
_deviceService = serviceProvider.GetRequiredService<IDeviceService>(); | ||
_tracer = serviceProvider.GetService<ITracer>(); | ||
} | ||
|
||
/// <summary>验证身份</summary> | ||
/// <param name="token"></param> | ||
/// <returns></returns> | ||
/// <exception cref="ApiException"></exception> | ||
protected override Boolean OnAuthorize(String token) | ||
{ | ||
if (!base.OnAuthorize(token) || Jwt == null || Jwt.Subject.IsNullOrEmpty()) return false; | ||
|
||
var dv = _deviceService.QueryDevice(Jwt.Subject); | ||
if (dv == null || !dv.Enable) throw new ApiException(ApiCode.Forbidden, "无效设备!"); | ||
|
||
_device = dv; | ||
_deviceService.Current = dv; | ||
|
||
return true; | ||
} | ||
#endregion | ||
|
||
#region 登录 | ||
/// <summary>设备登录</summary> | ||
/// <param name="request"></param> | ||
/// <returns></returns> | ||
[AllowAnonymous] | ||
[HttpPost(nameof(Login))] | ||
public virtual ILoginResponse Login(ILoginRequest request) => _deviceService.Login(request, "Http", UserHost); | ||
|
||
/// <summary>设备注销</summary> | ||
/// <param name="reason">注销原因</param> | ||
/// <returns></returns> | ||
[HttpGet(nameof(Logout))] | ||
public virtual ILogoutResponse Logout(String reason) | ||
{ | ||
var device = _deviceService.Logout(reason, "Http", UserHost); | ||
|
||
return new LogoutResponse | ||
{ | ||
Name = device?.Name, | ||
Token = null, | ||
}; | ||
} | ||
#endregion | ||
|
||
#region 心跳 | ||
/// <summary>设备心跳</summary> | ||
/// <param name="request"></param> | ||
/// <returns></returns> | ||
[HttpPost(nameof(Ping))] | ||
public virtual IPingResponse Ping(IPingRequest request) | ||
{ | ||
var rs = new PingResponse | ||
{ | ||
Time = request.Time, | ||
ServerTime = DateTime.UtcNow.ToLong(), | ||
}; | ||
|
||
var device = _device; | ||
if (device != null) | ||
{ | ||
//rs.Period = device.Period; | ||
|
||
_deviceService.Ping(request, Token, UserHost); | ||
|
||
// 令牌有效期检查,10分钟内到期的令牌,颁发新令牌。 | ||
// 这里将来由客户端提交刷新令牌,才能颁发新的访问令牌。 | ||
var tm = _deviceService.ValidAndIssueToken(device.Code, Token); | ||
if (tm != null) | ||
rs.Token = tm.AccessToken; | ||
} | ||
|
||
return rs; | ||
} | ||
|
||
/// <summary>设备心跳</summary> | ||
/// <returns></returns> | ||
[HttpGet(nameof(Ping))] | ||
public virtual IPingResponse Ping() => new PingResponse() { Time = 0, ServerTime = DateTime.UtcNow.ToLong(), }; | ||
#endregion | ||
|
||
#region 升级 | ||
/// <summary>升级检查</summary> | ||
/// <returns></returns> | ||
[HttpGet(nameof(Upgrade))] | ||
public virtual IUpgradeInfo Upgrade() | ||
{ | ||
var device = _device ?? throw new ApiException(ApiCode.Unauthorized, "节点未登录"); | ||
|
||
//throw new NotImplementedException(); | ||
return new UpgradeInfo { }; | ||
} | ||
#endregion | ||
|
||
#region 下行通知 | ||
/// <summary>下行通知</summary> | ||
/// <returns></returns> | ||
[HttpGet(nameof(Notify))] | ||
public virtual async Task Notify() | ||
{ | ||
if (Token.IsNullOrEmpty()) | ||
{ | ||
HttpContext.Response.StatusCode = (Int32)HttpStatusCode.Unauthorized; | ||
return; | ||
} | ||
|
||
if (HttpContext.WebSockets.IsWebSocketRequest) | ||
{ | ||
using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync(); | ||
|
||
await Handle(socket, Token); | ||
} | ||
else | ||
HttpContext.Response.StatusCode = 400; | ||
} | ||
|
||
private async Task Handle(WebSocket socket, String token) | ||
{ | ||
var device = _device ?? throw new InvalidOperationException("未登录!"); | ||
|
||
_deviceService.WriteHistory(device, "WebSocket连接", true, socket.State + "", UserHost); | ||
|
||
var source = new CancellationTokenSource(); | ||
var queue = _deviceService.GetQueue(device.Code); | ||
_ = Task.Run(() => socket.ConsumeAndPushAsync(queue, onProcess: null, source)); | ||
await socket.WaitForClose(null, source); | ||
} | ||
#endregion | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using NewLife.Caching; | ||
using NewLife.Remoting.Models; | ||
|
||
namespace NewLife.Remoting.Extensions.Services; | ||
|
||
/// <summary>设备会话服务。务必按Scope注入</summary> | ||
/// <remarks> | ||
/// 星尘的Node节点;IoT的Device设备; | ||
/// </remarks> | ||
public interface IDeviceService | ||
{ | ||
/// <summary>当前设备</summary> | ||
IDevice Current { get; set; } | ||
|
||
/// <summary>查找设备</summary> | ||
/// <param name="code"></param> | ||
/// <returns></returns> | ||
IDevice QueryDevice(String code); | ||
|
||
/// <summary>设备登录</summary> | ||
/// <param name="request"></param> | ||
/// <param name="source"></param> | ||
/// <param name="ip"></param> | ||
/// <returns></returns> | ||
LoginResponse Login(ILoginRequest request, String source, String ip); | ||
|
||
/// <summary>设备注销</summary> | ||
/// <param name="reason"></param> | ||
/// <param name="source"></param> | ||
/// <param name="ip"></param> | ||
/// <returns></returns> | ||
IDevice Logout(String reason, String source, String ip); | ||
|
||
/// <summary>设备心跳</summary> | ||
/// <param name="request"></param> | ||
/// <param name="token"></param> | ||
/// <param name="ip"></param> | ||
/// <returns></returns> | ||
Object Ping(IPingRequest request, String? token, String ip); | ||
|
||
/// <summary>验证并重新颁发令牌</summary> | ||
/// <param name="deviceCode"></param> | ||
/// <param name="token"></param> | ||
/// <returns></returns> | ||
TokenModel ValidAndIssueToken(String deviceCode, String? token); | ||
|
||
/// <summary> | ||
/// 获取指定设备的命令队列 | ||
/// </summary> | ||
/// <param name="deviceCode"></param> | ||
/// <returns></returns> | ||
IProducerConsumer<String> GetQueue(String deviceCode); | ||
|
||
/// <summary>写入历史</summary> | ||
/// <param name="device"></param> | ||
/// <param name="action"></param> | ||
/// <param name="success"></param> | ||
/// <param name="remark"></param> | ||
/// <param name="ip"></param> | ||
void WriteHistory(IDevice device, String action, Boolean success, String remark, String ip); | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace NewLife.Remoting.Models; | ||
|
||
/// <summary>设备接口</summary> | ||
public interface IDevice | ||
{ | ||
/// <summary>编码</summary> | ||
String Code { get; set; } | ||
|
||
/// <summary>名称</summary> | ||
String Name { get; set; } | ||
|
||
/// <summary>启用</summary> | ||
Boolean Enable { get;set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.