Developing a Blog Project Based on .NetCore StarBlog - (32) First Season Finale
This article documents the current status of the StarBlog project and the development plan for the upcoming v2 version.
目录19 个章节
Preface
This article records the current status of the StarBlog project and the development plans for the upcoming v2 version.
The StarBlog project has been around for over two years since its inception in 2022. I should have wrapped up a summary for the first phase much earlier, but for blog-type projects like this, once they become stable and usable, I don't have much motivation to keep updating them 😂
PS: By the way, there's also a Vue frontend series for StarBlog. I wrote it a while back but never published it. I'll be releasing it in installments moving forward—those interested can stay tuned.
The content is relatively simple (since I was learning Vue while building it), but from this perspective, it's quite suitable for beginners to read, as it's written from a novice's perspective.
The original intention behind developing this project was to learn by doing, serving as a practice project to get familiar with AspNetCore. Honestly, I can no longer dedicate much time to developing such blog projects... after all, this type of project is quite basic.
I recall the new projects I planned at the beginning of the year—progress on them has been limited so far. So, I need to focus my time on these new projects instead.
Here's a review of the new project development plans:
- StarSSO - Single Sign-On Service
- EchoSubs - Video Subtitle Recognition and Translation Service
- SnapMix - Random Image API Service
- AIHub 2.0 - An upgraded version of AIHub
- StarBlogHub - Implement a decentralized blog aggregation platform where different personal blogs can connect and share traffic
- TodayTV - Watch TV, intended to replace traditional live TV broadcasting
- Clipify - A Blazor-based video editing tool, already open-sourced
Looking at this list, it's quite a lot—spreading the effort too thin might make it hard to wrap things up…
However, focusing on other projects doesn't mean abandoning StarBlog. In fact, I've been making minor fixes and improvements consistently—this can be seen from the GitHub commits.
Shortcomings of the Current Version
Since the StarBlog project went live, I have continuously learned detailed knowledge about AspNetCore. Compared to when I first developed this project, my familiarity with the framework has improved somewhat, and naturally, I have also discovered limitations in the previous code:
- The "read" part of CRUD operations should use the
PATCHmethod. - Add
[HttpHead]to theGetmethod interface to support the HEAD method. - The filtering and search interfaces need to
trimthe parameters. - The return values of interfaces should not all be changed to the
ApiResponsetype. Instead, the framework'sActionResulttype should be retained to provide more functionality. - Only the return values of interfaces were unified, but exceptions were not wrapped. The
app.UseExceptionHandlermiddleware should be used to implement unified error handling (exception filters can also be used). - Support for markdown features like table of contents (TOC), formulas, code blocks, and tables with nested images is still poor.
These issues will be addressed in version 2.
v2 Version Planning
Currently, some new features and optimizations have been planned, but this is definitely not everything for the v2 version. If everyone has good suggestions, feel free to leave a comment and discuss!
Blog Frontend Restructuring
- Restructured using Next.js
- Markdown parsing using nodejs technology stack
Admin Dashboard Refactoring
- Refactor using a React-based technology stack
New Access Statistics Features
- Geographic Information Visualization
- Search Engine Indexing Analysis
- Anti-crawling Feature (反爬虫功能)
- Article View Count Statistics
Article Editing Features
- Use a new markdown editor (preferably a WYSIWYG editor like Wagtail)
- Support adding more content in articles (such as videos)
Article Reading Experience Optimization
- Use a new markdown rendering tool (currently using my forked and modified editor.md, which works okay, but this tool is quite old and no longer maintained. I hope to find a well-maintained, more modern rendering tool to replace it)
Article Encryption
- Set a fixed password
- Follow the official account to get a dynamic password
New Search Functionality
- Uses a full-text search engine
- Incorporates Embedding
AI Features
- Knowledge Base
- Conversational Features
- Article AI Summary
- Automatic Comments
AspNetCore: Revisiting the Basics
In early 2024, I reviewed some features of the AspNetCore framework again. The review was quite scattered and not systematic, but it is closely related to the development of StarBlog, so I decided to document it here in this article~
Unified Error Handling
Exception Filter
Writing the Filter
public class ApiExceptionFilter : IExceptionFilter {
public void OnException(ExceptionContext context) {
var response = new ApiResponse {
StatusCode = 500,
Successful = false,
// Modify here as needed to provide more detailed error information
Message = context.Exception.Message
};
context.Result = new ObjectResult(response) {
StatusCode = 500
};
// Mark the exception as handled
context.ExceptionHandled = true;
}
}
Registration
builder.Services.AddControllers(options => {
options.Filters.Add<ApiExceptionFilter>();
options.Filters.Add<ApiResponseFilter>();
});
The filter can capture exceptions thrown within an Action method or a controller, allowing developers to handle them and return a unified response format.
However, it cannot capture exceptions thrown outside of an Action method or controller. For example, accessing an endpoint decorated with the [Authorize] attribute without providing authentication information results in a 401 error, which this filter cannot catch.
Middleware
If you want to handle exceptions throughout the entire application, using middleware might be a better choice. Middleware can capture all types of exceptions that occur in the request processing pipeline.
Use the app.UseExceptionHandler middleware to implement unified error handling.
A simple example: Configure the built-in ExceptionHandler middleware in Program.cs.
if (app.Environment.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
app.UseExceptionHandler(applicationBuilder => {
applicationBuilder.Run(async context => {
// Log or perform other actions
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
await context.Response.WriteAsJsonAsync(new { message = "Unexpected error!" });
});
});
}
Writing your own Middleware
public class ErrorHandlingMiddleware {
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next) {
_next = next;
}
public async Task Invoke(HttpContext context) {
try {
await _next(context);
}
catch (Exception ex) {
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception) {
var response = new ApiResponse {
StatusCode = 500,
Successful = false,
Message = exception.Message
};
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(JsonConvert.SerializeObject(response));
}
}
Using the Middleware
app.UseMiddleware<ErrorHandlingMiddleware>();
Customizing Authentication and Authorization Return Values
In ASP.NET Core, when using the app.UseAuthentication() and app.UseAuthorization() middleware to handle authentication and authorization logic, if authentication or authorization fails, these middleware components directly modify the response to return HTTP status codes such as 401 (Unauthorized) or 403 (Forbidden).
These responses are not handled through the exception mechanism, so standard exception-handling middleware or UseExceptionHandler cannot capture or modify these specific error responses.
To customize these error responses, you need to configure the authentication middleware to use specific events to modify the response.
This typically involves adding event-handling logic in the authentication scheme's configuration. The following example uses JWT authentication to illustrate how to customize 401 and 403 responses:
Configuring JWT Authentication to Customize 401 and 403 Responses
Add event configuration inside services.AddAuthentication().AddJwtBearer(options => { ... }):
options.Events = new JwtBearerEvents {
OnChallenge = async context => {
if (!context.Response.HasStarted) {
context.HandleResponse(); // Prevent the default 401 response
var response = new ApiResponse {
StatusCode = 401,
Successful = false,
Message = "Authentication failed. You are not authorized."
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
}
},
OnForbidden = async context => {
var response = new ApiResponse {
StatusCode = 403,
Successful = false,
Message = "You do not have permission to access this resource."
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
}
};
In the JWT authentication flow, the JwtBearerEvents class provides several events to handle different authentication-related scenarios:
- OnChallenge - This event is triggered when authentication fails, typically because a valid JWT token was not provided in the request. For example, this event is triggered if the request lacks a token, the token is in an unexpected format, or the token has expired. The
OnChallengeevent is the correct place to handle returning a 401 Unauthorized response. - OnAuthenticationFailed - This event is triggered when an exception occurs during the authentication process. This usually involves errors in token parsing or validation, such as a tampered token. In this event, you can log the exception or modify the logic for handling authentication failures.
- OnForbidden - This event is triggered when a user is authenticated but does not meet specific authorization conditions. For example, a user's roles or permissions are insufficient to access a certain resource. In this event, you can customize the response to return a 403 Forbidden.
Custom Model Binding
Implementing the IModelBinder interface allows you to customize the model binding behavior for a controller action. This is called a Custom Model Binder and is recommended to be placed in the Helpers directory.
Example: Accept parameters in the format guid1,guid2,guid3,guid4 to retrieve a list of companies.
[HttpGet("{ids}")]
public async Task<IActionResult> GetCompanyCollection([FromRoute] IEnumerable<Guid> ids) {
}
Create a new file Helpers/ArrayModelBinder.cs.
public class ArrayModelBinder : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (!bindingContext.ModelMetadata.IsEnumerableType) {
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString();
if (string.IsNullOrWhiteSpace(value)) {
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
// Use reflection to get the parameter type.
var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeParameters[0];
// Create a new type converter based on the parameter type.
var converter = TypeDescriptor.GetConverter(elementType);
// Split the string by `,`,
// Specify `StringSplitOptions.RemoveEmptyEntries` to remove empty values after splitting, e.g., `1,,3,4` -> `["1", "3", "4"]`.
var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => converter.ConvertFromString(x.Trim()))
.ToArray();
var typedValues = Array.CreateInstance(elementType, values.Length);
values.CopyTo(typedValues, 0);
bindingContext.Model = typedValues;
bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
return Task.CompletedTask;
}
}
Finally, specify the ModelBinder to be used in the action signature.
[HttpGet("{ids}")]
public async Task<IActionResult> GetCompanyCollection(
[FromRoute]
[ModelBinder(BinderType = typeof(ArrayModelBinder))]
IEnumerable<Guid> ids) {
}
PS: To more intuitively show that this route parameter supports multiple values, you can add parentheses around the parameter.
[HttpGet("({ids})")]
public async Task<IActionResult> GetCompanyCollection(
Summary
Not sure what to say—I rarely write C# nowadays. Recently .NET 9 seems to have been released, with optimizations to the GC algorithm (GC 算法). I'll give it a try when I get a chance~
_EOF_
Developing a Blog Project Based on .NetCore StarBlog - (32) First Season Finale
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
StarBlog博客开发笔记(8):分类层级结构展示
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入 基于.NetCore开发博客项目 StarBl
2022-05-24 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(25):图片接口与文件上传
前言 上传文件的接口设计有两种风格,一种是整个项目只设置一个接口用来上传,然后其他需要用到文件的地方,都只存一个引用ID;另一种是每个需要文件的地方单独管理各自的文件。这俩各有优劣吧,本项目中选择的是后者的风格,文章图片和照片模块又要能CRUD又要批量导入,还是各自管理文件比较好。 图片接口 说会正题,先介绍一下图片相关接口。 图片列表 首先CRUD是肯定有的,图片列表的分页查看也是有的,不过因为
2023-02-05 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(29):添加RSS订阅功能
RSS 的对立面是算法推荐,像微信公众号、知乎、微博、今日头条等平台。 且不说算法推送平台广告多,迁移麻烦的问题。算法推荐的特点是,你不需要刻意选择,算法会根据你的喜好,给你推送内容。这样一来,你几乎没有选择的余地,在不断被「喂饱」中逐渐失去判断的能力。
2023-07-04 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(3):模型设计
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 ... 基于.NetCore开发博客项目 StarBlog - 接口返回值包装 基于.NetCore开发博客项目 StarBlog - 上传
2022-04-22 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…