博客 / 詳情

返回

C#.NET ControllerBase 深入解析:Web API 控制器的核心基石

簡介

ControllerBaseASP.NET Core 中構建 Web API 控制器的基類,位於 Microsoft.AspNetCore.Mvc 命名空間。它提供了豐富的功能來處理 HTTP 請求,但不包含視圖支持。

核心功能:

  • HTTP 響應:提供方法(如 Ok、NotFound)生成標準 HTTP 響應。
  • 模型綁定:自動將請求數據綁定到參數(如查詢字符串、請求體)。
  • 驗證支持:結合 [ApiController] 實現自動模型驗證。
  • 異步友好:支持 async/await 處理高併發請求。
  • 輕量設計:不包含 MVC 視圖相關的功能,適合 API 場景。

ControllerBase 的定位

  • 位於命名空間 Microsoft.AspNetCore.Mvc,是所有 Web API 控制器的基類。
  • 與 MVC 的 Controller 相比,ControllerBase 不包含視圖(Razor)相關功能,僅提供 內容結果、狀態碼、路由綁定、模型驗證 等 Web API 核心支持。
  • 通常搭配 [ApiController] 特性使用,開啓自動模型驗證、參數綁定規則和返回行為。

繼承體系

object
 └─ ControllerBase
     ├─ Controller         // 包含 View()、PartialView() 等 MVC 視圖方法
     └─ 用户自定義 Controllers…
  • ControllerBase

    • 提供結果工廠方法(Ok(), CreatedAtAction() 等)
    • 包含屬性如 Request、Response、ModelState、User
    • 實現接口 IActionFilterMetadata、IActionResult 等元數據標記
  • Controller(典型用於 MVC

    • 繼承自 ControllerBase 並額外添加視圖渲染方法

構造注入與內置屬性

在自定義 API 控制器中,通常這樣聲明:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _svc;
    public ProductsController(IProductService svc)
    {
        _svc = svc;
    }
    // Action 方法…
}
  • [ApiController]

    • 自動執行模型驗證,錯誤時直接返回 400
    • 簡化參數綁定(如 [FromBody]、[FromQuery] 默認行為)
  • Route、HttpXXX 特性

    • 定義路由模板與 HTTP 動作要匹配的請求

常用內置屬性

屬性 説明
HttpContext 當前請求上下文
Request 快速訪問 HttpContext.Request
Response 快速訪問 HttpContext.Response
User 當前認證用户的 ClaimsPrincipal
RouteData 路由參數及其綁定值
ModelState 模型驗證狀態與錯誤詳情

結果工廠方法

ControllerBase 提供一系列返回 ActionResult 的方法,用於快速構造 HTTP 響應:

成功響應

方法 HTTP 狀態碼 説明
Ok() / Ok(object value) 200 返回 200,帶或不帶響應體
Created(string uri, object value) 201 資源已創建,Location 頭部指定新資源 URI
CreatedAtAction(...) / CreatedAtRoute(...) 201 指定路由或動作生成 Location
NoContent() 204 成功但無響應體

客户端錯誤

方法 HTTP 狀態碼 説明
BadRequest() / BadRequest(object error) 400 返回模型驗證錯誤或自定義錯誤消息
Unauthorized() 401 未認證
Forbid() 403 無訪問權限
NotFound() / NotFound(object value) 404 資源未找到
Conflict() 409 衝突,如重複資源
UnsupportedMediaType() 415 不支持的媒體類型

服務端錯誤

方法 HTTP 狀態碼 説明
StatusCode(int statusCode) 自定義 返回任意狀態碼
Problem() 500+ 按 RFC 7807 生成標準化錯誤響應體
Tip: 這些工廠方法本質上返回了實現 IActionResult 的對象,框架在管道末端執行並寫入 HTTP。

核心功能詳解

HTTP 響應方法

狀態碼響應

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    var product = _repository.Get(id);
    
    if (product == null)
        return NotFound(); // 404
    
    return Ok(product); // 200 + 數據
}

文件響應

[HttpGet("download/{fileName}")]
public IActionResult DownloadFile(string fileName)
{
    var filePath = Path.Combine(_env.ContentRootPath, "Files", fileName);
    
    if (!System.IO.File.Exists(filePath))
        return NotFound();
    
    var fileBytes = System.IO.File.ReadAllBytes(filePath);
    return File(fileBytes, "application/octet-stream", fileName);
}

請求上下文訪問

常用屬性

public class ProductsController : ControllerBase
{
    [HttpGet("info")]
    public IActionResult GetInfo()
    {
        // 獲取請求信息
        var method = Request.Method;
        var contentType = Request.ContentType;
        
        // 獲取用户信息
        var userName = User.Identity?.Name;
        var isAdmin = User.IsInRole("Admin");
        
        // 獲取路由數據
        var routeId = RouteData.Values["id"];
        
        return Ok(new { method, userName });
    }
}

模型綁定與驗證

  • 自動驗證(啓用 [ApiController] 時)

    • Action 執行前對標註了驗證特性的模型進行驗證
    • ModelState.IsValid == false 則自動返回 400 Bad Request,響應體包含錯誤詳情
[HttpPost]
public IActionResult CreateProduct(
    [FromBody] Product product, // 從JSON綁定
    [FromQuery] string category, // 從查詢字符串綁定
    [FromHeader] string apiKey) // 從請求頭綁定
{
    // 自動模型驗證
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    
    // 業務邏輯處理
    var createdProduct = _service.Create(product, category);
    
    return CreatedAtAction(nameof(GetProduct), 
        new { id = createdProduct.Id }, createdProduct);
}

高級功能與最佳實踐

操作結果包裝

自定義統一響應格式

public class ApiResult<T> : IActionResult
{
    public T Data { get; }
    public int StatusCode { get; }
    public string? Message { get; }

    public ApiResult(T data, int statusCode = 200, string? message = null)
    {
        Data = data;
        StatusCode = statusCode;
        Message = message;
    }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        var result = new ObjectResult(new
        {
            Success = StatusCode >= 200 && StatusCode < 300,
            Data,
            Message,
            Timestamp = DateTime.UtcNow
        })
        {
            StatusCode = StatusCode
        };

        await result.ExecuteResultAsync(context);
    }
}

// 在控制器中使用
[HttpGet("{id}")]
public ApiResult<Product> GetProduct(int id)
{
    var product = _repository.Get(id);
    return product == null 
        ? new ApiResult<Product>(null, 404, "Product not found") 
        : new ApiResult<Product>(product);
}

內容協商

支持多種響應格式

[ApiController]
[Route("api/[controller]")]
[Produces("application/json", "application/xml")] // 支持的響應類型
public class ProductsController : ControllerBase
{
    [HttpGet]
    public ActionResult<IEnumerable<Product>> GetAll()
    {
        return _repository.GetAll();
    }
}

API 版本控制

實現 API 版本管理

[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    [MapToApiVersion("1.0")]
    public IEnumerable<Product> GetV1()
    {
        // 版本1.0的實現
    }

    [HttpGet]
    [MapToApiVersion("2.0")]
    public ActionResult<IEnumerable<ProductDto>> GetV2()
    {
        // 版本2.0的實現
    }
}

異步支持

  • ControllerBase 建議寫 異步 Action,返回 Task<IActionResult> 或直接 Task<T>(結合泛型 ActionResult<T>):
[HttpGet("{id}")]
public async Task<ActionResult<Product>> Get(int id)
{
    var p = await _svc.FindAsync(id);
    return p is null ? NotFound() : Ok(p);
}
  • ActionResult<T>(.NET Core 2.1+)

    • 允許方法既返回 T(自動包裝為 200)也返回 ActionResult(可自定義狀態碼)。

過濾器與管道

  • ControllerBase 支持註冊和應用各種 過濾器:

    • 授權過濾器([Authorize]
    • 資源過濾器(IResourceFilter
    • 動作過濾器(IActionFilter
    • 異常過濾器(IExceptionFilter
    • 結果過濾器(IResultFilter
  • 示例:全局註冊
services.AddControllers(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});
  • 示例:單控制器/Action 應用
[TypeFilter(typeof(LogActionFilter))]
public class MyController : ControllerBase { … }

安全最佳實踐

身份驗證與授權

[ApiController]
[Authorize] // 控制器級別要求認證
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    [HttpGet]
    [Authorize(Roles = "Admin")] // 要求管理員角色
    public IActionResult GetAllOrders() { /* ... */ }

    [HttpGet("my")]
    public IActionResult GetMyOrders()
    {
        // 獲取當前用户ID
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        return Ok(_repository.GetOrdersByUser(userId));
    }

    [HttpPost]
    [Authorize(Policy = "RequireVerifiedEmail")] // 使用自定義策略
    public IActionResult CreateOrder([FromBody] Order order) { /* ... */ }
}

跨域資源共享 (CORS)

[ApiController]
[Route("api/[controller]")]
[EnableCors("AllowSpecificOrigin")] // 啓用CORS策略
public class PublicDataController : ControllerBase
{
    [HttpGet]
    public IActionResult GetPublicData() { /* ... */ }
}

// 在Startup中配置CORS策略
services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigin",
        builder => builder.WithOrigins("https://example.com")
                          .AllowAnyMethod()
                          .AllowAnyHeader());
});

常見問題解決方案

模型驗證統一處理

// 創建統一驗證過濾器
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(new
            {
                Code = 400,
                Message = "參數驗證失敗",
                Errors = context.ModelState
                    .SelectMany(m => m.Value.Errors)
                    .Select(e => e.ErrorMessage)
            });
        }
    }
}

// 全局註冊
services.AddControllers(options => 
{
    options.Filters.Add<ValidateModelAttribute>();
});

全局異常處理

public class ApiExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        var statusCode = exception switch
        {
            NotFoundException => 404,
            ValidationException => 400,
            UnauthorizedAccessException => 401,
            _ => 500
        };

        context.Result = new ObjectResult(new
        {
            Code = statusCode,
            Message = exception.Message
        })
        {
            StatusCode = statusCode
        };
        
        context.ExceptionHandled = true;
    }
}

ControllerBase 最佳實踐總結

API 設計規範

// 使用HTTP動詞特性
[HttpGet("{id}")]
[HttpPost]
[HttpPut("{id}")]
[HttpDelete("{id}")]

響應類型聲

[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetProduct(int id) { /* ... */ }

異步優先

public async Task<IActionResult> Get() { /* ... */ }

依賴注入

public MyController(IMyService service) { /* ... */ }

安全加固

[Authorize]
[ValidateAntiForgeryToken]
[EnableCors("SafePolicy")]

版本管理

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]

統一響應格式

return Ok(new { Data = result, Message = "Success" });

資源和文檔

  • 官方文檔:

    • Microsoft Learn:https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers
    • ControllerBase:https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnet...
  • NuGet 包:https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core
  • GitHub:https://github.com/dotnet/aspnetcore
user avatar admim 頭像 veronicaaa 頭像 yayahonghong 頭像 xvrzhao 頭像 pipiimmortal 頭像 0xboo 頭像
6 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.