一、什么是Filter
在 ASP.NET Core 中,Filter 是一种用于处理 HTTP 请求和响应的机制,它可以让你在请求和响应的不同阶段添加额外的逻辑。Filter 在 MVC(Model-View-Controller)和 Web API 应用程序中起着重要作用,可以帮助你实现诸如身份验证、授权、日志记录、异常处理等功能。
二、 创建自定义的 Action Filter
/// <summary>
/// 自定义的 Action Filter,用于在动作方法执行前后记录日志
/// </summary>
public class CustomActionFilter : ActionFilterAttribute
{
private static readonly ILog log = LogManager.GetLogger(typeof(CustomActionFilter));
/// <summary>
/// Id
/// </summary>
private string SysId { get; set; }
/// <summary>
/// 密钥
/// </summary>
private string SysSecret { get; set; }
/// <summary>
/// 构造函数,接收系统Id和密钥
/// </summary>
/// <param name="sysId"></param>
/// <param name="sysSecret"></param>
public CustomActionFilter(string sysId, string sysSecret)
{
this.SysId = sysId;
this.SysSecret = sysSecret;
}
/// <summary>
/// Action执行前的逻辑
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
log.Info("Action执行前...");
var request = context.HttpContext.Request;
string sysId = GetRequestValue(context, SysId);
string sysSecret = GetRequestValue(context, SysSecret);
if (string.IsNullOrEmpty(sysId) || string.IsNullOrEmpty(sysSecret))
{
//context.Result = new UnauthorizedObjectResult("签名无效!");
//return;
}
#region sysId 和 sysSecret 都不为空的情况下执行的操作(建议这存储到数据库)
// 执行并记录日志
FilterLog filterLog = new FilterLog()
{
Name = context.Controller.GetType().Name,
RequestUrl = GetUrl(request),
RequestMethod = request.Method,
RequestBody = await GetBodyAsStringAsync(request),
RequestTime = DateTime.Now,
RequestIpAddress = context.HttpContext.Connection.RemoteIpAddress?.ToString(),
};
string json = JsonConvert.SerializeObject(filterLog);
log.Info($"日志: {json}");
#endregion
await next();
}
/// <summary>
/// Action执行后的逻辑
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
log.Info("Action执行后...");
await next();
}
/// <summary>
/// 获取请求参数的值
/// </summary>
/// <param name="actionContext"></param>
/// <param name="paramName"></param>
/// <returns></returns>
private string GetRequestValue(ActionExecutingContext actionContext, string paramName)
{
HttpRequest request = actionContext.HttpContext.Request;
if (request == null)
return null;
if (request.ContentType != null && request.ContentType.IndexOf("multipart/form-data", StringComparison.OrdinalIgnoreCase) >= 0)
{
return null;
}
foreach (object value in actionContext.ActionArguments.Values)
{
var dict = JObject.Parse(JsonConvert.SerializeObject(value));
if (dict != null && dict.ContainsKey(paramName))
return dict[paramName].ToString();
}
return null;
}
/// <summary>
/// 获取请求的URL
/// </summary>
/// <param name="httpRequest"></param>
/// <returns></returns>
private static string GetUrl(HttpRequest httpRequest)
{
StringBuilder builder = new StringBuilder();
if (!string.IsNullOrWhiteSpace(httpRequest.Scheme))
{
builder.Append(httpRequest.Scheme);
builder.Append("://");
}
builder.Append(httpRequest.Host.ToUriComponent());
builder.Append(httpRequest.PathBase.ToUriComponent());
builder.Append(httpRequest.Path.ToUriComponent());
builder.Append(httpRequest.QueryString.ToUriComponent());
return builder.ToString();
}
/// <summary>
/// 异步获取请求主体的内容
/// </summary>
/// <param name="httpRequest"></param>
/// <returns></returns>
public static async Task<string> GetBodyAsStringAsync(HttpRequest httpRequest)
{
if (httpRequest.Body == null)
{
return null;
}
if (!httpRequest.Body.CanSeek)
{
httpRequest.EnableBuffering();
}
var currentPosition = httpRequest.Body.Position;
httpRequest.Body.Position = 0;
string requestBody;
using (var reader = new StreamReader(httpRequest.Body, leaveOpen: true))
{
requestBody = await reader.ReadToEndAsync();
}
httpRequest.Body.Position = currentPosition;
return requestBody;
}
}
三、 为控制器或动作方法应用过滤器加上[CustomActionFilter("sysId", "sysSecret")]
接口就是简单EF core的增删改查
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using TodoApiCore6._0.Tools.Context;
using TodoApiCore6._0.Models;
using TodoApiCore6._0.Models.DTO;
using Microsoft.EntityFrameworkCore;
using TodoApiCore6._0.Models.ApiResult;
using Microsoft.Data.SqlClient;
using TodoApiCore6._0.Tools.Filter;
namespace TodoApiCore6._0.Controllers
{
[ApiController]
[Route("[controller]")]
[CustomActionFilter("sysId", "sysSecret")]
public class TodoController : Controller
{
private readonly TodoContext _context;
private readonly IMapper _mapper;
public TodoController(TodoContext context, IMapper mapper, IHttpContextAccessor accessor)
{
_context = context;
_mapper = mapper;
if (_context.TodoItems.Count() == 0)
{
// 如果集合为空,则创建新的 TodoItem,
_context.TodoItems.Add(new TodoItem { Name = "Item1", IsComplete = false });
_context.TodoItems.Add(new TodoItem { Name = "Item2", IsComplete = false });
_context.TodoItems.Add(new TodoItem { Name = "Item3", IsComplete = true });
_context.SaveChanges();
}
accessor.HttpContext.Request.EnableBuffering();
}
/// <summary>
/// 查询所有列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("All")]
public async Task<ActionResult<ApiResult>> GetAll()
{
//int id = Convert.ToInt32(this.User.FindFirst("Id").Value);
return ApiResultHelper.Success(await _context.TodoItems.ToArrayAsync());
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="baseQuery"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<ApiResult>> GetPagination([FromQuery] BaseQuery baseQuery)
{
#region 方式一(一次性加载全部数据并进行分页查询可能会导致性能问题)
//IEnumerable<TodoItem> list = await _context.TodoItems.ToArrayAsync();
//if (baseQuery.Key == null)
//{
// return ApiResultHelper.Success(list.OrderByDescending(o => o.Id).Distinct().Skip((baseQuery.Page - 1) * baseQuery.Size).Take(baseQuery.Size), list.Count());
//}
//return ApiResultHelper.Success(list.Where(o => o.Name.Contains(baseQuery.Key)).OrderByDescending(o => o.Id).Distinct().Skip((baseQuery.Page - 1) * baseQuery.Size).Take(baseQuery.Size), list.Count());
#endregion
#region 方式二(推荐使用数据库的分页查询功能)
string query = "SELECT * FROM TodoItems";
if (baseQuery.Key != null)
{
query += " WHERE Name LIKE '%' + @Keyword + '%'";
}
query += " ORDER BY Id DESC OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";
var parameters = new List<SqlParameter>
{
new SqlParameter("@Offset", (baseQuery.Page - 1) * baseQuery.Size),
new SqlParameter("@PageSize", baseQuery.Size)
};
if (baseQuery.Key != null)
{
parameters.Add(new SqlParameter("@Keyword", baseQuery.Key));
}
var currentData = await _context.TodoItems.FromSqlRaw(query, parameters.ToArray()).ToListAsync();
int currentDataCount = currentData.Count();
return ApiResultHelper.Success(currentData, currentDataCount);
#endregion
}
/// <summary>
/// 根据Id查找数据
/// </summary>
/// <param name="id">序号</param>
/// <returns></returns>
[HttpGet("{id}")]
public async Task<ActionResult<ApiResult>> GetByID(int id)
{
var item = await _context.TodoItems.FirstOrDefaultAsync(t => t.Id == id);
var todoItemDTO = _mapper.Map<TodoItemDto>(item);
return ApiResultHelper.Success(todoItemDTO);
}
/// <summary>
/// 创建
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<ApiResult>> Create([FromBody] TodoItem item)
{
if (item == null)
{
return BadRequest();
}
else
{
_context.TodoItems.Add(item);
await _context.SaveChangesAsync();
return ApiResultHelper.Success(item);
}
}
/// <summary>
/// 修改
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<ActionResult<ApiResult>> Update([FromBody] TodoItem item)
{
HttpContext.Request.EnableBuffering();
if (item == null)
{
return BadRequest();
}
else
{
var todo = _context.TodoItems.SingleOrDefault(t => t.Id == item.Id);
if (todo == null)
{
return NotFound();
}
else
{
todo.IsComplete = item.IsComplete;
todo.Name = item.Name;
_context.TodoItems.Update(todo);
await _context.SaveChangesAsync();
return ApiResultHelper.Success(todo);
}
}
}
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("{id}")]
public async Task<ActionResult<ApiResult>> Delete(int id)
{
var todo = _context.TodoItems.SingleOrDefault(t => t.Id == id);
if (todo == null)
{
return NotFound();
}
else
{
_context.TodoItems.Remove(todo);
await _context.SaveChangesAsync();
return ApiResultHelper.Success(todo);
}
}
}
}
// 在Program.cs如下代码,如果不添加获取不到RequestBody内容
#region 添加HttpContextAccessor到服务容器中
builder.Services.AddHttpContextAccessor();
#endregion
四、 日志效果