Biwen.QuickApi.Contents
2.0.5
dotnet add package Biwen.QuickApi.Contents --version 2.0.5
NuGet\Install-Package Biwen.QuickApi.Contents -Version 2.0.5
<PackageReference Include="Biwen.QuickApi.Contents" Version="2.0.5" />
<PackageVersion Include="Biwen.QuickApi.Contents" Version="2.0.5" />
<PackageReference Include="Biwen.QuickApi.Contents" />
paket add Biwen.QuickApi.Contents --version 2.0.5
#r "nuget: Biwen.QuickApi.Contents, 2.0.5"
#addin nuget:?package=Biwen.QuickApi.Contents&version=2.0.5
#tool nuget:?package=Biwen.QuickApi.Contents&version=2.0.5
Biwen.QuickApi.Contents
Biwen.QuickApi.Contents是基于Biwen.QuickApi
用于内容管理的库,可以帮助您轻松地在项目中添加内容管理系统(CMS)功能,支持不同类型的内容字段、内容版本控制、审计和渲染。
功能特性
- 灵活的内容模型定义
- 多种字段类型支持(文本、数字、日期、图片、文件等)
- 内容版本控制
- 内容审计日志
- 内容渲染服务
- 支持内容草稿和发布工作流
- 基于Elasticsearch的全文搜索功能
快速入门
1. 安装
在您的项目中引用Biwen.QuickApi.Contents包:
dotnet add package Biwen.QuickApi.Contents
2. 配置数据库上下文
创建一个实现IContentDbContext
接口的数据库上下文:
public class YourDbContext : DbContext, IContentDbContext
{
public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
}
public DbSet<Content> Contents { get; set; }
public DbSet<ContentAuditLog> ContentAuditLogs { get; set; }
public DbSet<ContentVersion> ContentVersions { get; set; }
public DbContext Context => this;
}
3. 在Program.cs中注册服务
// 配置Elasticsearch客户端(可选,如需使用搜索功能)
builder.Services.AddSingleton(sp =>
{
return new ElasticsearchClient(new Uri(builder.Configuration["ElasticSearch"]!));
});
// 添加内容搜索服务(可选,如需使用搜索功能)
builder.Services.AddScoped<IContentSearchService, ElasticsearchService>();
// 添加BiweContents核心服务
builder.Services.AddBiwenContents<YourDbContext>(options =>
{
// 是否启用Api接口
options.EnableApi = true; // 默认启用
// 是否生成OpenApi文档
options.GenerateApiDocument = true; // 默认启用
// 自定义配置
options.ViewPath = "\\Views\\Contents"; // 设置内容视图路径
options.PermissionValidator = httpContext =>
{
// 您的权限验证逻辑
return Task.FromResult(httpContext.User.Identity!.IsAuthenticated);
};
});
// 应用程序启动后初始化Elasticsearch索引(可选)
app.Lifetime.ApplicationStarted.Register(async () =>
{
using var scope = app.Services.CreateScope();
var searchService = scope.ServiceProvider.GetService<IContentSearchService>();
if (searchService != null)
{
await searchService.InitializeIndexAsync();
}
});
4. 定义内容模型
创建继承于ContentBase<T>
的内容模型:
public class BlogPost : ContentBase<BlogPost>
{
[Required]
public TextFieldType Title { get; set; } = new();
public UrlFieldType Slug { get; set; } = new();
[MarkdownToolBar(MarkdownToolStyle.Standard)]
public MarkdownFieldType Content { get; set; } = new();
public DateTimeFieldType PublishDate { get; set; } = new();
public ImageFieldType FeaturedImage { get; set; } = new();
// 使用枚举类型作为选项
public enum CategoryType
{
技术 = 1,
新闻 = 2,
生活 = 3
}
public OptionsFieldType<CategoryType> Category { get; set; } = new();
// 多选项也使用枚举类型
[Flags]
public enum TagType
{
[Description(".NET")]
DotNet = 1,
CSharp = 2,
AspNetCore = 4,
Frontend = 8,
Backend = 16
}
public OptionsMultiFieldType<TagType> Tags { get; set; } = new();
}
使用内容仓储
创建或更新内容
public class BlogService
{
private readonly IContentRepository _contentRepository;
public BlogService(IContentRepository contentRepository)
{
_contentRepository = contentRepository;
}
public async Task<Guid> CreateBlogPostAsync(BlogPost post)
{
// 创建内容并保存
return await _contentRepository.SaveContentAsync(post, post.Title.Value, post.Slug.Value);
}
public async Task UpdateBlogPostAsync(Guid id, BlogPost post)
{
// 更新内容
await _contentRepository.UpdateContentAsync(id, post);
}
}
查询内容
// 通过ID获取内容
var post = await _contentRepository.GetContentAsync<BlogPost>(id);
// 获取特定类型的所有内容分页列表
var pagedPosts = await _contentRepository.GetContentsByTypeAsync<BlogPost>(pageIndex: 0, len: 10);
// 通过slug获取内容
var postBySlug = await _contentRepository.GetContentsByTypeAsync<BlogPost>("my-blog-post");
发布内容
// 设置内容状态为已发布
await _contentRepository.SetContentStatusAsync(id, ContentStatus.Published);
删除内容
await _contentRepository.DeleteContentAsync(id);
内容字段类型
Biwen.QuickApi.Contents支持以下字段类型:
TextFieldType
- 短文本字段TextAreaFieldType
- 长文本字段MarkdownFieldType
- Markdown富文本IntegerFieldType
- 整数NumberFieldType
- 浮点数BooleanFieldType
- 布尔值DateTimeFieldType
- 日期时间TimeFieldType
- 时间UrlFieldType
- URL链接ColorFieldType
- 颜色ImageFieldType
- 图片FileFieldType
- 文件OptionsFieldType<T>
- 单选项(枚举)OptionsMultiFieldType<T>
- 多选项(枚举)ArrayFieldType
- 数组类型
内容渲染
您可以使用IDocumentRenderService
来渲染内容:
public class BlogController : Controller
{
private readonly IContentRepository _contentRepository;
private readonly IDocumentRenderService _renderService;
public BlogController(
IContentRepository contentRepository,
IDocumentRenderService renderService)
{
_contentRepository = contentRepository;
_renderService = renderService;
}
public async Task<IActionResult> ViewBlogPost(string slug)
{
var post = await _contentRepository.GetContentsByTypeAsync<BlogPost>(slug);
if (post == null)
return NotFound();
// 渲染内容,使用指定的视图模板
var renderedContent = await _renderService.RenderAsync(post, "BlogPost");
return View("BlogPost", renderedContent);
}
}
自定义文档类型视图
Biwen.QuickApi.Contents 允许您为每种内容类型创建自定义视图模板,以控制内容的呈现方式。
视图位置规范
视图文件应遵循以下命名和位置规范:
- 基础路径:视图文件默认位于
Views/Contents
目录下(可通过BiwenContentOptions.ViewPath
配置) - 视图命名:视图名称应与内容类型的简单名称匹配,例如
BlogPost.cshtml
- 子目录支持:您可以在
Views/Contents
下创建子目录来组织不同类别的视图
创建自定义视图模板
以下是创建自定义视图模板的步骤:
- 在您的项目中创建
Views/Contents
目录(如果不存在) - 创建与您的内容类型名称相匹配的 .cshtml 文件,例如
BlogPost.cshtml
- 在视图文件中添加必要的命名空间和模型指令
示例视图模板 Views/Contents/BlogPost.cshtml
:
@model Biwen.QuickApi.Contents.Rendering.ContentViewModel<YourNamespace.BlogPost>
@{
ViewData["Title"] = Model.Content.Title.Value;
Layout = "_Layout"; // 使用您的布局文件
}
<div class="blog-post">
<h1>@Model.Content.Title.Value</h1>
<div class="metadata">
<span class="date">发布于: @Model.Content.PublishDate.Value.ToString("yyyy-MM-dd")</span>
<span class="category">分类: @Model.Content.Category.DisplayValue</span>
</div>
@if (Model.Content.FeaturedImage != null && !string.IsNullOrEmpty(Model.Content.FeaturedImage.Value))
{
<div class="featured-image">
<img src="@Model.Content.FeaturedImage.Value" alt="@Model.Content.Title.Value" />
</div>
}
<div class="content markdown-body">
@Html.Raw(Model.Content.Content.Html)
</div>
@if (Model.Content.Tags != null && Model.Content.Tags.DisplayValues?.Any() == true)
{
<div class="tags">
<strong>标签:</strong>
@foreach (var tag in Model.Content.Tags.DisplayValues)
{
<span class="tag">@tag</span>
}
</div>
}
</div>
视图模型结构
视图接收 ContentViewModel<T>
类型的模型,该模型包含两个主要属性:
Content
:您的强类型内容实例(如BlogPost
)ContentDefine
:内容的元数据信息(ID、创建时间、状态等)
访问字段值
在视图中访问内容字段值的方法:
- 文本字段:
Model.Content.Title.Value
- Markdown字段:
Html.Raw(Model.Content.Content.Html)
(已转换为HTML) - 日期字段:
Model.Content.PublishDate.Value.ToString("yyyy-MM-dd")
- 选项字段(单选):
Model.Content.Category.DisplayValue
- 选项字段(多选):
Model.Content.Tags.DisplayValues
(字符串集合)
自定义CSS样式
为确保内容呈现一致,您可以引入特定的样式:
@section Styles {
<link rel="stylesheet" href="~/css/markdown.css" />
<link rel="stylesheet" href="~/css/blog-post.css" />
}
条件渲染
您可以根据内容的特定字段值进行条件渲染:
@if (Model.ContentDefine.Status == ContentStatus.Published)
{
<div class="published-badge">已发布</div>
}
else if (Model.ContentDefine.Status == ContentStatus.Draft)
{
<div class="draft-badge">草稿</div>
}
内容版本和审计
系统自动记录内容的变更历史:
// 获取内容的版本历史
var versions = await _contentVersionService.GetVersionsAsync(contentId);
// 查看内容审计日志
var auditLogs = await _contentAuditLogService.GetAuditLogsAsync(contentId);
权限控制
您可以通过配置BiwenContentOptions
中的PermissionValidator
来控制对内容管理功能的访问权限:
services.AddBiwenContents<YourDbContext>(options =>
{
options.PermissionValidator = async httpContext =>
{
// 检查用户是否有管理员角色
return httpContext.User.IsInRole("Admin");
};
});
最佳实践
- 为每种内容类型创建专门的内容模型
- 使用内容版本控制来跟踪内容变更
- 为不同的内容类型创建专门的视图模板
- 利用内容状态工作流来管理发布流程
- 使用内容事件来实现自定义业务逻辑
内容搜索服务
Biwen.QuickApi.Contents提供基于Elasticsearch的内容搜索服务,支持全文搜索、分面搜索、高亮显示等高级功能。
自动索引更新
内容模块集成了自动索引更新功能,当内容被创建、更新、删除或状态发生变化时,系统会自动更新ElasticSearch索引。这是通过事件处理机制实现的,即使在应用程序没有启用ElasticSearch服务的情况下也不会产生错误。
事件处理器支持以下操作:
- 当内容创建后,自动添加到索引
- 当内容更新后,自动更新索引
- 当内容删除后,自动从索引中移除
- 当内容状态变更为已归档时,自动从索引中移除
配置Elasticsearch服务
- 在
appsettings.json
中添加Elasticsearch连接配置:
{
"ElasticSearch": "http://localhost:9200"
}
- 在项目的启动配置中注册Elasticsearch客户端和搜索服务:
// 添加Elasticsearch客户端
builder.Services.AddSingleton(sp =>
{
return new ElasticsearchClient(new Uri(builder.Configuration["ElasticSearch"]!));
});
// 添加内容搜索服务
builder.Services.AddScoped<IContentSearchService, ElasticsearchService>();
- 应用程序启动时初始化索引:
// 在应用程序启动时初始化Elasticsearch索引
app.UseEndpoints(endpoints =>
{
// 其他端点配置...
// 异步初始化Elasticsearch索引
var scope = app.ApplicationServices.CreateScope();
var searchService = scope.ServiceProvider.GetRequiredService<IContentSearchService>();
_ = searchService.InitializeIndexAsync();
});
使用内容搜索服务
public class SearchController : Controller
{
private readonly IContentSearchService _searchService;
private readonly IContentRepository _contentRepository;
public SearchController(IContentSearchService searchService, IContentRepository contentRepository)
{
_searchService = searchService;
_contentRepository = contentRepository;
}
// 重建索引
public async Task<IActionResult> RebuildIndex()
{
// 获取所有内容
var allContents = await _contentRepository.GetAllContentsAsync();
// 重建索引
await _searchService.RebuildIndexAsync(allContents);
return Ok("索引重建完成");
}
// 执行搜索
public async Task<IActionResult> Search(string query, int page = 1, int size = 10, string filter = null, string sort = null)
{
// 执行搜索查询
var results = await _searchService.SearchContentsAsync(
query,
page,
size,
filter,
sort,
enableHighlight: true,
facets: ["contentType"] // 返回内容类型分面
);
return View(results);
}
}
搜索功能特性
搜索服务支持以下功能:
- 全文搜索:支持跨字段的全文搜索,可以搜索内容标题、Slug以及内容字段的值。
- 模糊搜索:使用Elasticsearch的模糊匹配功能,容忍拼写错误。
- 高亮显示:自动高亮显示搜索结果中与查询相关的部分。
- 分面搜索:支持按内容类型、状态等属性进行分面聚合。
- 排序和过滤:支持按创建时间、更新时间、标题等字段排序,并支持多种过滤方式。
搜索查询语法
搜索服务支持多种查询和过滤语法:
- 基础搜索:直接输入关键词,例如:
博客
- 类型过滤:按内容类型过滤,有两种格式:
contentType:BlogPost
contentType = 'BlogPost'
- 状态过滤:按内容状态过滤,例如:
status:Published
- 字段筛选:按特定字段和值过滤,例如:
field:Category=技术文章
- 数值范围查询:按数值字段范围过滤,例如:
range:Price=gte:100,lte:200
- 支持的操作符:
gte
(大于等于)、lte
(小于等于)、gt
(大于)、lt
(小于)
- 支持的操作符:
- 日期范围查询:按日期字段范围过滤,例如:
daterange:EventDate=gte:2024-01-01,lte:2024-12-31
- 支持的操作符:
gte
(大于等于)、lte
(小于等于)、gt
(大于)、lt
(小于)
- 支持的操作符:
- 布尔条件查询:按布尔字段过滤,例如:
bool:IsActive=true
- 排序:支持多种排序选项:
createdAt:asc
- 按创建时间升序createdAt:desc
- 按创建时间降序updatedAt:asc
- 按更新时间升序updatedAt:desc
- 按更新时间降序title:asc
- 按标题字母升序title:desc
- 按标题字母降序
嵌套字段查询示例
以下是一些嵌套字段查询的具体示例:
- 数值范围查询:
// 查询价格在150到250之间的产品
var result = await _searchService.SearchContentsAsync("", filter: "range:Price=gte:150,lte:250");
- 日期范围查询:
// 查询2024年上半年的活动
var result = await _searchService.SearchContentsAsync("", filter: "daterange:EventDate=gte:2024-01-01,lte:2024-06-30");
- 布尔条件查询:
// 查询激活状态的产品
var result = await _searchService.SearchContentsAsync("", filter: "bool:IsActive=true");
- 组合查询:
// 查询2024年上半年的激活状态产品,价格在100到200之间
var result = await _searchService.SearchContentsAsync("",
filter: "daterange:EventDate=gte:2024-01-01,lte:2024-06-30 AND bool:IsActive=true AND range:Price=gte:100,lte:200");
这些查询都支持嵌套字段,可以精确匹配 JsonContent
中的字段值。查询结果会自动过滤出符合所有条件的文档。
常见问题
Q: 如何自定义内容字段类型?
A: 您可以创建实现IFieldType
接口的自定义字段类型,并在服务注册时添加它:
public class CustomFieldType : IFieldType
{
// 实现接口方法
}
// 注册
services.AddSingleton<IFieldType, CustomFieldType>();
Q: 如何处理内容关联关系?
A: 您可以使用自定义字段类型来存储关联ID,或者在内容模型中使用特殊的引用字段。
Q: 如何优化Elasticsearch搜索性能?
A: 可以考虑以下几点:
- 在生产环境中增加分片数和副本数
- 为高频查询创建专用索引
- 使用筛选而非查询来提高性能
- 定期重建索引以优化存储
通过Slug访问内容
Biwen.QuickApi.Contents提供了一个便捷的方法,使您可以通过自定义URL格式访问内容。您可以在应用程序启动时配置这个功能:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 其他中间件配置...
app.UseEndpoints(endpoints =>
{
// 添加通过Slug访问内容的路由
// 这将创建形如 /{prefix}/{slug} 的路由
endpoints.MapBiwenContentsBySlug("p"); // 使用"p"作为URL前缀
// 其他路由配置...
});
}
上面的配置将创建如下格式的URL:/p/{slug}
,例如/p/my-blog-post
。当用户访问这个URL时,系统会自动查找匹配的内容并使用IDocumentRenderService
进行渲染。
您可以根据需要自定义前缀,例如使用"contents"、"articles"、"pages"等。
API 接口说明
Biwen.QuickApi.Contents 提供了一系列 API 接口,用于内容的增删改查等操作。所有 API 接口都位于 ~/contents
路径下(由 Constants.GroupName
定义)。
内容查询 API
GET ~~/contents/infopages
用于查询内容列表,支持分页和过滤。
查询参数:
pageNumber
- 页码,从1开始,默认:1pageSize
- 分页大小,默认:10contentType
- 文档类型,必填slug
- Slug,如果提供则进行精确匹配title
- 按标题过滤status
- 状态过滤,0:草稿,1:已发布,2:归档
示例请求:
GET ~~/contents/infopages?contentType=BlogPost&pageNumber=1&pageSize=10&status=1
获取指定内容 API
GET ~~/contents/{id:guid}
根据 ID 获取指定的内容详情。
路径参数:
id
- 内容的 GUID
示例请求:
GET ~~/contents/5f9c7b4e-3c1a-4f5b-9d1a-6c2a5e34b7d9
创建内容 API
POST ~~/contents/create
创建新的内容。
请求体:
{
"title": "内容标题",
"slug": "content-slug",
"contentType": "BlogPost",
"jsonContent": "[{\"fieldName\":\"Title\",\"value\":\"hello world\"}]"
}
字段说明:
title
- 内容标题slug
- 内容的 URL 友好标识符contentType
- 内容类型的完全限定名或最后一个名称jsonContent
- 序列化的内容,为 JSON 字符串,格式为[{ "fieldName": "字段名称", "value": "字段值" }]
更新内容 API
PUT ~~/contents/update/{id:guid}
更新已有内容。
路径参数:
id
- 内容的 GUID
请求体:
{
"title": "更新后的标题",
"slug": "updated-slug",
"status": 1,
"jsonContent": "更新后的序列化内容"
}
设置内容状态 API
PUT ~~/contents/status/{id:guid}
设置内容的状态,如发布或归档。
路径参数:
id
- 内容的 GUID
请求体:
{
"status": 1
}
状态值:
0
- 草稿1
- 已发布2
- 归档
删除内容 API
DELETE ~~/contents/{id:guid}
删除指定的内容。
路径参数:
id
- 内容的 GUID
预览内容 API
GET ~~/contents/preview/{id:guid}
预览指定内容,不改变内容状态。
路径参数:
id
- 内容的 GUID
返回:
- 返回HTML格式的内容预览
内容版本管理 API
获取内容版本列表
GET ~~/contents/versions/{id:guid}
获取指定内容的所有历史版本。
路径参数:
id
- 内容的 GUID
返回:
- 返回内容版本列表,包含版本ID、创建时间和版本说明等信息
获取指定版本内容
GET ~~/contents/versions/{id:guid}/{version:guid}
获取内容的特定历史版本。
路径参数:
id
- 内容的 GUIDversion
- 版本的 GUID
返回:
- 返回指定版本的内容快照
回滚内容版本
POST ~~/contents/versions/{id:guid}/rollback/{version:guid}
将内容回滚到指定的历史版本。
路径参数:
id
- 内容的 GUIDversion
- 要回滚到的版本 GUID
返回:
- 操作成功返回
true
内容审计日志 API
获取内容审计日志
GET ~~/contents/{id:guid}/auditlogs
获取指定内容的所有审计日志记录。
路径参数:
id
- 内容的 GUID
返回:
- 返回内容的所有审计日志记录,包含操作类型、操作时间和操作者等信息
按时间范围查询审计日志
GET ~~/contents/auditlogs
按时间范围查询系统中的内容审计日志。
查询参数:
startTime
- 开始时间,默认为当天开始endTime
- 结束时间,默认为当前时间pageNumber
- 页码,从1开始pageSize
- 每页记录数
返回:
- 返回符合条件的审计日志分页列表
更多资源
- 查看示例项目以获取更多用法示例
- 参考API文档获取详细信息
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Biwen.AutoClassGen.Attributes (>= 1.8.0)
- Biwen.QuickApi (>= 2.0.5)
- Elastic.Clients.Elasticsearch (>= 8.18.0)
-
net9.0
- Biwen.AutoClassGen.Attributes (>= 1.8.0)
- Biwen.QuickApi (>= 2.0.5)
- Elastic.Clients.Elasticsearch (>= 8.18.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.0.5 | 202 | 5/14/2025 |