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博客开发笔记:上传图片功能
后端实现,前端的后面说 可以先在swagger里测试 ImageSharp组件文档:https://docs.sixlabors.com/articles/imagesharp/gettingstarted.html service private string GetPhotoFilePath(Photo photo) { return Path.Combine(_environment
2022-02-28 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记:部署
Nginx 反向代理 后端配置中间件 app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); Nginx配置 server { listen
2022-12-05 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(2):环境准备和创建项目
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 ... 基于.NetCore开发博客项目 StarBlog - 接口返回值包装 基于.NetCore开发博客项目 StarBlog - 上传图片功能 基于.NetCore开发博客项目 StarBlog - 身份认证
2022-04-22 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…