傳統的 asp.net mvc 對應著 .netcore 中的 asp.net core mvc,可以利用 asp.net core mvc 去構建跨平臺,可擴充套件,高效能的web應用和 api 介面。
程式設計師都有一些潔癖,很多時候我們都想很完美的包裝一些錯誤資訊,如一些返回空response的request請求,或者一些 action 中返回 null value 的情況,通常這些情況下,asp.net core mvc 都會返回 http status 204 (No Content),在本篇中,我準備修改一下這種從 action 返回 null value 的預設行為。
在 Asp.NET Core 中新建 Controller在解決方案視窗中的 Controller 資料夾上右鍵並選擇 Add -> Controller
去新建Controller,指定這個 Controller 的名字為 DemoController,接下來用下面的程式碼替換 DemoController。
[Route("api/[controller]")] [ApiController] public class DemoController : ControllerBase { readonly Repository repository = new Repository(); [HttpGet] public ActionResult Get() { string item = repository.GetMessage(); return Ok(item); } [HttpGet("{id}", Name = "Get")] public IActionResult Get(int id) { string item = repository.GetMessage(); return Ok(item); } }
建立一個 Repository下面是一個 Repository 類,裡面包含了一個返回 null 的 GetMessage 方法,當然這僅僅是為了演示目的。
public class Repository { public string GetMessage() { return null; } }
在 asp.net core mvc 中如何處理 null 值
當用 httpGet 的方式去請求 DemoController 的 GetMessage 方法,mvc 會返回 Http Status 204 (No Content),如下圖所示:
為什麼會這樣呢? 當response準備返回時,asp.net core mvc 會從當前可用的 格式化器 列表中選擇一個合適的去處理當前的 response 物件,比如說:這個格式化器可以是 Json formatter,又可以是 Xml formatter,或者任何合適於該資源的 formatter。
對了,當遇到 null 值時,asp.net core mvc 框架會使用一個叫做 HttpNoContentOutputFormatter
,它的職責就是將 null 轉換成 Http Status 204(No Content
),下面展示了原始碼實現:
public class HttpNoContentOutputFormatter : IOutputFormatter{ public Task WriteAsync(OutputFormatterWriteContext context) { HttpResponse response = context.HttpContext.Response; response.ContentLength = 0L; if (response.StatusCode == 200) { response.StatusCode = 204; } return Task.CompletedTask; }}
禁用 HttpNoContentOutputFormatter
如果你好奇的話,可以把 HttpNoContentOutputFormatter 禁用掉,這樣就切斷了 asp.net mvc core 處理 null 值的預設行為,如果要這麼做的話,在 Startup 類的 ConfigureServices 方法做如下配置。
services.AddMvc(f => { f.OutputFormatters.RemoveType (typeof(HttpNoContentOutputFormatter)); f.OutputFormatters.Insert(0, new HttpNoContentOutputFormatter { TreatNullValueAsNoContent = false });});
上面的程式碼禁用了 http status 204
的行為,取而代之的就是返回 http status 200 (OK),然後 null 值會被塞到 response body 中。
為了能夠達到404的效果,我來更新一下 action 的名字,下面就是 DemoController 更新後的程式碼片段:
[Route("api/[controller]")] [ApiController] public class DemoController : ControllerBase { readonly Repository repository = new Repository(); [HttpGet] public ActionResult Get() { string item = repository.GetMessage(); if (item == null) return NotFound(); return Ok(item); } }
當你再次請求 DemoController 時,框架會返回 http status 404(Not Found),如下面圖片所示:
一個更完善的的返回 http 404 的方式一個更好的返回 http status 404 的方式是使用 action filter 或者 result filter,如下程式碼:
public class NotFoundActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext context) { if (context.Result is ObjectResult) { ObjectResult objectResult = context.Result as ObjectResult; if (objectResult.Value == null) context.Result = new NotFoundResult(); } } }
你可以將這個 filter 放置在 action級別,controller級別 或者 全域性級別,如果你要放到全域性級別,可以在 Startup 的 ConfigureServices 方法中新增如下程式碼:
public void ConfigureServices(IServiceCollection services){ services.AddMvc(f => { f.Filters.Add(new NotFoundResultFilterAttribute()); });}
當在使用 asp.net core mvc 時,你可以在 action 返回值處使用 IActionResult
或 ActionResult<T>
或其他任何物件,在 Action 返回資料後,框架底層會對你的返回結果做必要的 序列化操作。
然而,當 action 返回 null 值時, asp.net core mvc 並不會嘗試用任何可用的序列化器去處理這個 null 值,換句話說,框架會返回 Http status 204,表示請求成功但無內容,幸運的是,你可以按需改變這個預設的行為。