You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core Web应用:如何创建Task包装器/中间件并复用异常处理逻辑?

Great question! Dealing with repetitive try-catch blocks and boilerplate logging across every action is such a common pain point in .NET Core apps. Let’s break down the cleanest approaches to solve this—including handling those tricky return type issues you mentioned.


最佳解决方案:自定义Action过滤器(推荐)

For .NET Core Web APIs/MVC apps, an async action filter is the most targeted approach. It hooks directly into the action execution pipeline, lets you handle exceptions globally, and simplifies return type management by wrapping responses in a consistent format.

1. Create the Exception Handling Filter

Implement IAsyncActionFilter to intercept action execution, catch exceptions, log them, and standardize responses:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

public class ExceptionHandlingFilter : IAsyncActionFilter
{
    private readonly ILogger<ExceptionHandlingFilter> _logger;

    public ExceptionHandlingFilter(ILogger<ExceptionHandlingFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        try
        {
            // Run the action and get its result
            var resultContext = await next();
            
            // Optional: Wrap successful responses in a consistent DTO
            if (resultContext.Result is ObjectResult objectResult)
            {
                resultContext.Result = new ObjectResult(new ApiResponse<object>
                {
                    Success = true,
                    Data = objectResult.Value,
                    Message = "Operation completed successfully"
                });
            }
        }
        catch (Exception ex)
        {
            // Log the full exception details
            _logger.LogError(ex, "Unhandled exception in action {ActionName}", context.ActionDescriptor.DisplayName);
            
            // Return a standardized error response
            var errorResponse = new ApiResponse<object>
            {
                Success = false,
                Message = ex.Message,
                ErrorCode = "INTERNAL_SERVER_ERROR"
            };
            
            context.Result = new ObjectResult(errorResponse)
            {
                StatusCode = StatusCodes.Status500InternalServerError
            };
        }
    }
}

// Generic API response DTO to unify return types
public class ApiResponse<T>
{
    public bool Success { get; set; }
    public T Data { get; set; }
    public string Message { get; set; }
    public string ErrorCode { get; set; }
}

2. Register the Filter Globally

Add the filter to your app's service configuration so it applies to all actions automatically:

// In Program.cs
builder.Services.AddControllers(options =>
{
    // Add the filter to the global filter collection
    options.Filters.Add<ExceptionHandlingFilter>();
});

通用Task包装器方案

If you need a reusable wrapper for any async task (not just actions), create a generic extension method to handle exceptions and logging:

using Microsoft.Extensions.Logging;

public static class TaskWrapperExtensions
{
    private static readonly ILogger<TaskWrapperExtensions> _logger = 
        LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<TaskWrapperExtensions>();

    // Wrapper for tasks with no return value
    public static async Task MyExceptionHandledAsyncTask(this Task task)
    {
        try
        {
            await task;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Async task failed");
            // Optionally rethrow with a custom exception or handle silently
            throw new CustomOperationException("Task execution failed", ex);
        }
    }

    // Wrapper for tasks with a return type (handles any generic T)
    public static async Task<T> MyExceptionHandledAsyncTask<T>(this Task<T> task)
    {
        try
        {
            return await task;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Async task with return type {Type} failed", typeof(T).Name);
            // Return a default value or error-wrapped object based on your needs
            return default;
        }
    }
}

Usage in an Action:

[HttpGet]
public async Task<IActionResult> FetchUserData(int userId)
{
    var userData = await _userService.GetUserByIdAsync(userId).MyExceptionHandledAsyncTask();
    return Ok(new ApiResponse<UserDto> { Success = true, Data = userData });
}

Request-Level Middleware

For a global, pipeline-wide exception handler (less action-specific but good for broad error handling), use a custom middleware:

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception during request processing");
            await HandleExceptionAsync(context, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;

        var errorResponse = new ApiResponse<object>
        {
            Success = false,
            Message = "An unexpected error occurred",
            ErrorCode = "INTERNAL_ERROR"
        };

        await context.Response.WriteAsJsonAsync(errorResponse);
    }
}

Register the Middleware:

// In Program.cs (place it early in the pipeline, before routing)
app.UseMiddleware<ExceptionHandlingMiddleware>();

Key Recommendations
  • Action Filter: Best for Web APIs/MVC—gives you access to action context (like parameters, action name) for precise logging, and standardizes return types effortlessly.
  • Task Wrapper: Use when you need to reuse exception handling across non-action async methods (e.g., service layer code).
  • Middleware: Ideal for catching unhandled exceptions that slip past action filters, but lacks action-specific context.

内容的提问来源于stack exchange,提问作者ferflores

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:00:01