一個模塊的 API層(Web層),主要負責如下幾個方面的工作:接收 前端層 提交的數據查詢請求,使用 服務層 提供的 IQueryable 查詢數據源,查詢出需要的數據返回前端。接收 前端層 提交的業務處理請求,調用 服務層 的服務,處理業務需求,並將操作結果返回前端。使用MVC的 Area-Co... ...
什麼是OSharp
OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個基於.NetStandard2.0
開發的一個.NetCore
快速開發框架。這個框架使用最新穩定版的.NetCore SDK
(當前是.NET Core 2.2),對 AspNetCore 的配置、依賴註入、日誌、緩存、實體框架、Mvc(WebApi)、身份認證、許可權授權等模塊進行更高一級的自動化封裝,並規範了一套業務實現的代碼結構與操作流程,使 .Net Core 框架更易於應用到實際項目開發中。
- 開源地址:https://github.com/i66soft/osharp
- 官方示例:https://www.osharp.org
- 文檔中心:https://docs.osharp.org
- VS 插件:https://marketplace.visualstudio.com/items?itemName=LiuliuSoft.osharp
- 系列示例:https://github.com/i66soft/osharp-docs-samples
概述
一個模塊的 API層(Web層),主要負責如下幾個方面的工作:
- 接收
前端層
提交的數據查詢請求,使用服務層
提供的IQueryable<T>
查詢數據源,查詢出需要的數據返回前端 - 接收
前端層
提交的業務處理請求,調用服務層
的服務,處理業務需求,並將操作結果返回前端 - 使用MVC的
Area-Controller-Action
的層次關係,聯合[ModuleInfo]
特性, 定義 Api模塊Module
的 樹形組織結構,API模塊 的 依賴關係,構建出Module
的樹形數據 - 定義 API 的
可訪問方式
,API的訪問方式可分為匿名訪問
,登錄訪問
和角色訪問
- 定義自動事務提交,涉及資料庫變更的業務,可在API定義自動事務提交,在業務層實現業務時即可不用考慮事務的問題
整個過程如下圖所示
API層 代碼佈局
API層 代碼佈局分析
API層 即是Web網站服務端的MVC控制器,控制器可按粒度需要不同,分為模塊控制器和單實體控制器,這個由業務需求決定。
通常,後臺管理的控制器,是實體粒度的,即每個實體都有一個控制器,並且存在於 /Areas/Admin/Controlers
文件夾內。
博客模塊的 API 層控制器,如下圖所示:
src 源代碼文件夾
└─Liuliu.Blogs.Web 項目Web工程
└─Areas 區域文件夾
└─Admin 管理區域文件夾
└─Controllers 管理控制器文件夾
└─Blogs 博客模塊文件夾
├─BlogController.cs 博客管理控制器
└─PostController.cs 文章管理控制器
API定義及訪問控制的基礎建設
API定義
API定義即MVC或WebApi的 Area-Controller-Action
定義,為方便及規範此步驟的工作,OSharp定義了一些 API基礎控制器基類
,繼承這些基類,很容易實現API定義。
ApiController
ApiController
用於非Area的Api控制器,基類添加了 操作審計[AuditOperation]
,[ApiController]
特性,並定義了一個 [Route("api/[controller]/[action]")]
的路由特性
/// <summary>
/// WebApi控制器基類
/// </summary>
[AuditOperation]
[ApiController]
[Route("api/[controller]/[action]")]
public abstract class ApiController : Controller
{
/// <summary>
/// 獲取或設置 日誌對象
/// </summary>
protected ILogger Logger => HttpContext.RequestServices.GetLogger(GetType());
}
AreaApiController
與 無區域控制器基類ApiController
相對應,對於區域控制器,也定義了一個基類 AreaApiController
/// <summary>
/// WebApi的區域控制器基類
/// </summary>
[AuditOperation]
[ApiController]
[Route("api/[area]/[controller]/[action]")]
public abstract class AreaApiController : Controller
{ }
AdminApiController
對於相當常用的 管理Admin
區域,也同樣定義了一個控制器基類AdminApiController
,此基類繼承於AreaApiController
,並添加了區域特性[Area("Admin")]
和角色訪問限制特性[RoleLimit]
[Area("Admin")]
[RoleLimit]
public abstract class AdminApiController : AreaApiController
{ }
博客模塊API實現
[Description("管理-博客信息")]
public class BlogController : AdminApiController
{ }
[Description("管理-文章信息")]
public class PostController : AdminApiController
{ }
Module樹形結構及依賴
ModuleInfoAttribute
為了描述 API的層級關係,OSharp定義了一個ModuleInfoAttribute
特性,把當前功能(Controller或者Action)封裝為一個模塊(Module)節點,可以設置模塊依賴的其他功能,模塊的位置信息等。此特性用於系統初始化時自動提取模塊樹信息Module。
/// <summary>
/// 描述把當前功能(Controller或者Action)封裝為一個模塊(Module)節點,可以設置模塊依賴的其他功能,模塊的位置信息等
/// 此特性用於系統初始化時自動提取模塊樹信息Module
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ModuleInfoAttribute : Attribute
{
/// <summary>
/// 獲取或設置 模塊名稱,為空則取功能名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 獲取或設置 模塊代碼,為空則取功能Action名
/// </summary>
public string Code { get; set; }
/// <summary>
/// 獲取或設置 層次序號
/// </summary>
public double Order { get; set; }
/// <summary>
/// 獲取或設置 模塊位置,父級模塊,模塊在樹節點的位置,預設取所在類的位置,需要在命名空間與當前類之間加模塊,才設置此值
/// </summary>
public string Position { get; set; }
/// <summary>
/// 獲取或設置 父級位置模塊名稱,需要在命名空間與當前類之間加模塊,才設置此值
/// </summary>
public string PositionName { get; set; }
}
[ModuleInfo]
特性主要有兩種用法:
- 在
Controller
上,主要控制模塊的順序Order
,模塊的位置Position
,模塊名稱PositionName
,例如:
[ModuleInfo(Position = "Blogs", PositionName = "博客模塊")]
[Description("管理-博客信息")]
public class BlogController : AdminApiController
{ }
- 在
Action
上,主要用於標註哪些Action是作為可許可權分配的API,通常無需使用屬性,例如:
/// <summary>
/// 讀取博客
/// </summary>
/// <returns>博客頁列表</returns>
[HttpPost]
[ModuleInfo]
[Description("讀取")]
public PageData<BlogOutputDto> Read(PageRequest request)
{ }
DependOnFunctionAttribute
由於業務的關聯性和UI的合理佈局,API功能點並 不是單獨存在 的,要完成一個完整的操作,各個API功能點可能會 存在依賴性。例如:
- 在要進行管理列表中的
新增、更新、刪除
等操作,首先要能進入列表,即列表數據的讀取
操作,那麼新增、更新、刪除
等操作就對讀取
操作存在依賴需求。 - 對於
新增、更新
操作,通常需要對數據進行唯一性驗證
,那麼也會存在依賴關係
為了在代碼中描述這些依賴關係,OSharp中定義了DependOnFunctionAttribute
特性,在Action上標註當前API對其他API(可跨Controller,跨Area)的依賴關係。
/// <summary>
/// 模塊依賴的功能信息,用於提取模塊信息Module時確定模塊依賴的功能(模塊依賴當前功能和此特性設置的其他功能)
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class DependOnFunctionAttribute : Attribute
{
/// <summary>
/// 初始化一個<see cref="DependOnFunctionAttribute"/>類型的新實例
/// </summary>
public DependOnFunctionAttribute(string action)
{
Action = action;
}
/// <summary>
/// 獲取或設置 區功能變數名稱稱,為null(不設置)則使用當前功能所在區域,如要表示無區域的功能,需設置為空字元串""
/// </summary>
public string Area { get; set; }
/// <summary>
/// 獲取或設置 控制器名稱,為null(不設置)則使用當前功能所在控制器
/// </summary>
public string Controller { get; set; }
/// <summary>
/// 獲取 功能名稱Action,不能為空
/// </summary>
public string Action { get; }
}
如下示例,表明管理列表中的新增文章
業務對文章讀取
有依賴關係
/// <summary>
/// 新增文章
/// </summary>
/// <param name="dtos">新增文章信息</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("新增")]
public async Task<AjaxResult> Create(PostInputDto[] dtos)
{ }
API訪問控制
API的訪問控制,分為三種:
- 匿名訪問
AllowAnonymousAttribute
:表示當前功能不需要登錄即可訪問,無視登錄狀態和角色要求 - 登錄訪問
LoginedAttribute
:表示當前功能需要登錄才能訪問,未登錄拒絕訪問 - 角色訪問
RoleLimitAttribute
:表示當前功能需要登錄並且用戶擁有指定角色,才能訪問,未登錄或者登錄但未擁有指定角色,拒絕訪問
API訪問控制的控制順序按照 就近原則,即離要執行的功能最近的那個限制生效。以Controller上的標註與Action上的標註為例:
- Controller無,Action無,不限制
- Controller有,Action無,以Controller為準
- Controller無,Action有,以Action為準
- Controller有,Action有,以Action為準
在AdminApiController
基類中,已經設置了[RoleLimit]
,表示Admin區域中的所有Controller和Action的預設訪問控制方式就是 角色訪問。
[Area("Admin")]
[RoleLimit]
public abstract class AdminApiController : AreaApiController
{ }
如想額外控制,則需要在實現Action的時候進行單獨配置
[HttpPost]
[ModuleInfo]
[Logined]
[Description("讀取")]
public PageData<BlogOutputDto> Read(PageRequest request)
{ }
自動事務提交
在傳統框架中,事務的提交是在業務層實現完業務操作之後即手動提交的,這種方式能更精準的控制事務的結束位置,但也有不能適用的情況,例如當一個業務涉及多個服務的時候,每個服務各自提交了事務,便無法保證所有操作在一個完整的事務上進行了。
為此,OSharp框架提出了一種新的事務提交方式:在Action中通過Mvc的Filter來自動提交事務。
自動提交事務是通過如下的UnitOfWorkAttribute
實現的:
/// <summary>
/// 自動事務提交過濾器,在<see cref="OnResultExecuted"/>方法中執行<see cref="IUnitOfWork.Commit()"/>進行事務提交
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
[Dependency(ServiceLifetime.Scoped, AddSelf = true)]
public class UnitOfWorkAttribute : ActionFilterAttribute
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
/// <summary>
/// 初始化一個<see cref="UnitOfWorkAttribute"/>類型的新實例
/// </summary>
public UnitOfWorkAttribute(IServiceProvider serviceProvider)
{
_unitOfWorkManager = serviceProvider.GetService<IUnitOfWorkManager>();
}
/// <summary>
/// 重寫方法,實現事務自動提交功能
/// </summary>
/// <param name="context"></param>
public override void OnResultExecuted(ResultExecutedContext context)
{
ScopedDictionary dict = context.HttpContext.RequestServices.GetService<ScopedDictionary>();
AjaxResultType type = AjaxResultType.Success;
string message = null;
if (context.Result is JsonResult result1)
{
if (result1.Value is AjaxResult ajax)
{
type = ajax.Type;
message = ajax.Content;
if (ajax.Successed())
{
_unitOfWorkManager?.Commit();
}
}
}
else if (context.Result is ObjectResult result2)
{
if (result2.Value is AjaxResult ajax)
{
type = ajax.Type;
message = ajax.Content;
if (ajax.Successed())
{
_unitOfWorkManager?.Commit();
}
}
else
{
_unitOfWorkManager?.Commit();
}
}
//普通請求
else if (context.HttpContext.Response.StatusCode >= 400)
{
switch (context.HttpContext.Response.StatusCode)
{
case 401:
type = AjaxResultType.UnAuth;
break;
case 403:
type = AjaxResultType.UnAuth;
break;
case 404:
type = AjaxResultType.UnAuth;
break;
case 423:
type = AjaxResultType.UnAuth;
break;
default:
type = AjaxResultType.Error;
break;
}
}
else
{
type = AjaxResultType.Success;
_unitOfWorkManager?.Commit();
}
if (dict.AuditOperation != null)
{
dict.AuditOperation.ResultType = type;
dict.AuditOperation.Message = message;
}
}
}
如一次請求中涉及數據的 新增、更新、刪除 操作時,在 Action 上添加 [ServiceFilter(typeof(UnitOfWorkAttribute))]
,即可實現事務自動提交。
/// <summary>
/// 新增文章
/// </summary>
/// <param name="dtos">新增文章信息</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("新增")]
public async Task<AjaxResult> Create(PostInputDto[] dtos)
{ }
AjaxReuslt
對於 前後端分離 的項目,前端向後端的請求都是通過 application/json
的方式來交互的,這就需要在後端對操作結果進行封裝。OSharp提供了AjaxResult
類來承載操作結果數據
/// <summary>
/// 表示Ajax操作結果
/// </summary>
public class AjaxResult
{
/// <summary>
/// 初始化一個<see cref="AjaxResult"/>類型的新實例
/// </summary>
public AjaxResult()
: this(null)
{ }
/// <summary>
/// 初始化一個<see cref="AjaxResult"/>類型的新實例
/// </summary>
public AjaxResult(string content, AjaxResultType type = AjaxResultType.Success, object data = null)
: this(content, data, type)
{ }
/// <summary>
/// 初始化一個<see cref="AjaxResult"/>類型的新實例
/// </summary>
public AjaxResult(string content, object data, AjaxResultType type = AjaxResultType.Success)
{
Type = type;
Content = content;
Data = data;
}
/// <summary>
/// 獲取或設置 Ajax操作結果類型
/// </summary>
public AjaxResultType Type { get; set; }
/// <summary>
/// 獲取或設置 消息內容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 獲取或設置 返回數據
/// </summary>
public object Data { get; set; }
/// <summary>
/// 是否成功
/// </summary>
public bool Successed()
{
return Type == AjaxResultType.Success;
}
/// <summary>
/// 是否錯誤
/// </summary>
public bool Error()
{
return Type == AjaxResultType.Error;
}
/// <summary>
/// 成功的AjaxResult
/// </summary>
public static AjaxResult Success(object data = null)
{
return new AjaxResult("操作執行成功", AjaxResultType.Success, data);
}
}
其中AjaxResultType
的可選項為:
/// <summary>
/// 表示 ajax 操作結果類型的枚舉
/// </summary>
public enum AjaxResultType
{
/// <summary>
/// 消息結果類型
/// </summary>
Info = 203,
/// <summary>
/// 成功結果類型
/// </summary>
Success = 200,
/// <summary>
/// 異常結果類型
/// </summary>
Error = 500,
/// <summary>
/// 用戶未登錄
/// </summary>
UnAuth = 401,
/// <summary>
/// 已登錄,但許可權不足
/// </summary>
Forbidden = 403,
/// <summary>
/// 資源未找到
/// </summary>
NoFound = 404,
/// <summary>
/// 資源被鎖定
/// </summary>
Locked = 423
}
業務服務層的操作結果OperationResult
,可以很輕鬆的轉換為AjaxResult
/// <summary>
/// 將業務操作結果轉ajax操作結果
/// </summary>
public static AjaxResult ToAjaxResult<T>(this OperationResult<T> result, Func<T, object> dataFunc = null)
{
string content = result.Message ?? result.ResultType.ToDescription();
AjaxResultType type = result.ResultType.ToAjaxResultType();
object data = dataFunc == null ? result.Data : dataFunc(result.Data);
return new AjaxResult(content, type, data);
}
/// <summary>
/// 將業務操作結果轉ajax操作結果
/// </summary>
public static AjaxResult ToAjaxResult(this OperationResult result)
{
string content = result.Message ?? result.ResultType.ToDescription();
AjaxResultType type = result.ResultType.ToAjaxResultType();
return new AjaxResult(content, type);
}
通過這些擴展方法,可以很簡潔的完成由OperationResult
到AjaxResult
的轉換
public async Task<AjaxResult> Creat(PostInputDto[] dtos)
{
OperationResult result = await _blogsContract.CreatePosts(dtos);
return result.ToAjaxResult();
}
博客模塊API實現
下麵,我們來綜合運用上面定義的基礎建設,來實現 博客模塊 的API層。
API層的實現代碼,將實現如下關鍵點:
- 定義各實體的 Controller 和 Action,使用
[Description]
特性來聲明各個功能點的顯示名稱 - 使用
[ModuleInfo]
特性來定義API模塊的樹形結構 - 使用
[DependOnFunction]
來定義各API模塊之間的依賴關係 - 在
AdminApiController
基類中,已經添加了[RoleLimit]
特性來配置所有Admin
區域的API都使用 角色限制 的訪問控制,如需特殊的訪問控制,可在 Action 上單獨配置 - 涉及實體
增加、更新、刪除
操作的業務,按需要添加[ServiceFilter(typeof(UnitOfWorkAttribute))]
特性來實現事務自動提交
!!! node
API模塊對角色的許可權分配,將在後臺管理界面中進行許可權分配。
博客 - BlogController
根據 <業務模塊設計#WebAPI層> 中對博客管理的定義,Blog實體的對外API定義如下表所示:
操作 | 訪問類型 | 操作角色 |
---|---|---|
讀取 | 角色訪問 | 博客管理員、博主 |
申請開通 | 登錄訪問 | 已登錄未開通博客的用戶 |
開通審核 | 角色訪問 | 博客管理員 |
更新 | 角色訪問 | 博客管理員、博主 |
實現代碼如下:
[ModuleInfo(Position = "Blogs", PositionName = "博客模塊")]
[Description("管理-博客信息")]
public class BlogController : AdminApiController
{
/// <summary>
/// 初始化一個<see cref="BlogController"/>類型的新實例
/// </summary>
public BlogController(IBlogsContract blogsContract,
IFilterService filterService)
{
BlogsContract = blogsContract;
FilterService = filterService;
}
/// <summary>
/// 獲取或設置 數據過濾服務對象
/// </summary>
protected IFilterService FilterService { get; }
/// <summary>
/// 獲取或設置 博客模塊業務契約對象
/// </summary>
protected IBlogsContract BlogsContract { get; }
/// <summary>
/// 讀取博客列表信息
/// </summary>
/// <param name="request">頁請求信息</param>
/// <returns>博客列表分頁信息</returns>
[HttpPost]
[ModuleInfo]
[Description("讀取")]
public PageData<BlogOutputDto> Read(PageRequest request)
{
Check.NotNull(request, nameof(request));
Expression<Func<Blog, bool>> predicate = FilterService.GetExpression<Blog>(request.FilterGroup);
var page = BlogsContract.Blogs.ToPage<Blog, BlogOutputDto>(predicate, request.PageCondition);
return page.ToPageData();
}
/// <summary>
/// 申請開通博客
/// </summary>
/// <param name="dto">博客輸入DTO</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("申請")]
public async Task<AjaxResult> Apply(BlogInputDto dto)
{
Check.NotNull(dto, nameof(dto));
OperationResult result = await BlogsContract.ApplyForBlog(dto);
return result.ToAjaxResult();
}
/// <summary>
/// 審核博客
/// </summary>
/// <param name="dto">博客輸入DTO</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("申請")]
public async Task<AjaxResult> Verify(BlogVerifyDto dto)
{
Check.NotNull(dto, nameof(dto));
OperationResult result = await BlogsContract.VerifyBlog(dto);
return result.ToAjaxResult();
}
/// <summary>
/// 更新博客信息
/// </summary>
/// <param name="dtos">博客信息輸入DTO</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("更新")]
public async Task<AjaxResult> Update(BlogInputDto[] dtos)
{
Check.NotNull(dtos, nameof(dtos));
OperationResult result = await BlogsContract.UpdateBlogs(dtos);
return result.ToAjaxResult();
}
}
文章 - PostController
根據 <業務模塊設計#WebAPI層> 中對文章管理的定義,Post實體的對外API定義如下表所示:
操作 | 訪問類型 | 操作角色 |
---|---|---|
讀取 | 角色訪問 | 博客管理員、博主 |
新增 | 角色訪問 | 博主 |
更新 | 角色訪問 | 博客管理員、博主 |
刪除 | 角色訪問 | 博客管理員、博主 |
實現代碼如下:
[ModuleInfo(Position = "Blogs", PositionName = "博客模塊")]
[Description("管理-文章信息")]
public class PostController : AdminApiController
{
/// <summary>
/// 初始化一個<see cref="PostController"/>類型的新實例
/// </summary>
public PostController(IBlogsContract blogsContract,
IFilterService filterService)
{
BlogsContract = blogsContract;
FilterService = filterService;
}
/// <summary>
/// 獲取或設置 數據過濾服務對象
/// </summary>
protected IFilterService FilterService { get; }
/// <summary>
/// 獲取或設置 博客模塊業務契約對象
/// </summary>
protected IBlogsContract BlogsContract { get; }
/// <summary>
/// 讀取文章列表信息
/// </summary>
/// <param name="request">頁請求信息</param>
/// <returns>文章列表分頁信息</returns>
[HttpPost]
[ModuleInfo]
[Description("讀取")]
public virtual PageData<PostOutputDto> Read(PageRequest request)
{
Check.NotNull(request, nameof(request));
Expression<Func<Post, bool>> predicate = FilterService.GetExpression<Post>(request.FilterGroup);
var page = BlogsContract.Posts.ToPage<Post, PostOutputDto>(predicate, request.PageCondition);
return page.ToPageData();
}
/// <summary>
/// 新增文章信息
/// </summary>
/// <param name="dtos">文章信息輸入DTO</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("新增")]
public virtual async Task<AjaxResult> Create(PostInputDto[] dtos)
{
Check.NotNull(dtos, nameof(dtos));
OperationResult result = await BlogsContract.CreatePosts(dtos);
return result.ToAjaxResult();
}
/// <summary>
/// 更新文章信息
/// </summary>
/// <param name="dtos">文章信息輸入DTO</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("更新")]
public virtual async Task<AjaxResult> Update(PostInputDto[] dtos)
{
Check.NotNull(dtos, nameof(dtos));
OperationResult result = await BlogsContract.UpdatePosts(dtos);
return result.ToAjaxResult();
}
/// <summary>
/// 刪除文章信息
/// </summary>
/// <param name="ids">文章信息編號</param>
/// <returns>JSON操作結果</returns>
[HttpPost]
[ModuleInfo]
[DependOnFunction("Read")]
[ServiceFilter(typeof(UnitOfWorkAttribute))]
[Description("刪除")]
public virtual async Task<AjaxResult> Delete(int[] ids)
{
Check.NotNull(ids, nameof(ids));
OperationResult result = await BlogsContract.DeletePosts(ids);
return result.ToAjaxResult();
}
}
至此,博客模塊的 API層代碼 實現完畢。
API數據展示
運行Liuliu.Blogs
項目的後端工程Liuliu.Blogs.Web
,框架初始化時將通過 反射讀取API層代碼結構,進行博客模塊的 API模塊Module
- API功能點Function
的數據初始化,並分配好 依賴關係,功能點的 訪問控制 等約束。
Swagger查看數據
在SwaggerUI中,我們可以看到生成的 API模塊
博客 - Blog
文章 - Post
後臺管理查看數據
運行前端的 Angular 工程,我們可以在後臺管理的 許可權安全/模塊管理 中,可看到 博客模塊
的模塊數據以及模塊分配的功能點信息
資料庫查看數據
打開資料庫管理工具,可以看到 Module 和 Function 兩個表的相關數據
資料庫中的 API模塊Module
資料庫中的 API功能點Function
博客模塊授權
相關角色和用戶
博客模塊相關角色
根據 <業務模塊設計#WebAPI層> 中對許可權控制的定義,我們需要創建兩個相關角色
- 博主:可申請博客,更新自己的博客,對自己的文章進行新增、更新、刪除操作
- 博客管理員:可審批、更新、刪除所有博客,對所有文章進行更新、刪除操作
新增的兩個角色如下:
名稱 | 備註 | 管理角色 | 預設 | 鎖定 |
---|---|---|---|---|
博客管理員 | 博客管理員角色 | 是 | 否 | 否 |
博主 | 博客主人角色 | 否 | 否 | 否 |
註冊兩個測試用戶,並給用戶分配角色
新增測試用戶如下:
用戶名 | 用戶昵稱 | 分配角色 |
---|---|---|
[email protected] | 博客管理員測試 | 博客管理員 |
[email protected] | 博主測試01 | 博主 |
[email protected] | 博主測試02 | 博主 |
功能許可權
給角色分配API模塊
API模塊Module
對應的是後端的API模塊,將Module分配給角色Role
,相應的Role即擁有Module的所有功能點Function
給
博客管理員
角色分配功能許可權
給
博主
角色分配功能許可權
功能許可權預覽
分配好之後,擁有特定角色的用戶,便擁有模塊所帶來的功能點許可權
博客管理員用戶功能許可權
- 博主用戶功能許可權
測試博主01
測試博主02
數據許可權
OSharp框架內預設提供 角色 - 實體 配對的數據許可權指派。
博客管理員
對於博客管理員
角色,博客管理員能管理 博客Blog
和 文章Post
的所有數據,沒有數據許可權的約束要求。
博主
對於博主
角色,博主只能查看並管理 自己的 博客與文章,有數據許可權的約束要求。對博主的數據許可權約束如下:
對博客的讀取、更新操作限制
UserId = @當前用戶
對文章的讀取、更新、刪除操作限制
UserId = @當前用戶
如此,對博客模塊的數據許可權約束分配完畢。
在下一節中,我們將完善前端項目,添加博客模塊的前端實現,你將看到一個完整的博客模塊實現。