ASP.NET Core Mvc中空返回值的處理方法詳解
前言
如果你是一個(gè)初學(xué)者開始學(xué)習(xí) ASP.NET 或 ASP.NET MVC, 你可能并不知道什么是. net Framework和. net ore。不用擔(dān)心!我建議您看下官方文檔https://docs.microsoft.com/zh-cn/aspnet/index , 您可以輕松地看到比較和差異。
.NET Core MVC在如何返回操作結(jié)果方面非常靈活的。
你可以返回一個(gè)實(shí)現(xiàn)IActionResult接口的對(duì)象, 比如我們熟知的ViewResult, FileResult, ContentResult等。
[HttpGet]
public IActionResult SayGood()
{
return Content("Good!");
}
當(dāng)然你還可以直接返回一個(gè)類的實(shí)例。
[HttpGet]
public string HelloWorld()
{
return "Hello World";
}
在.NET Core 2.1中, 你還可以返回一個(gè)ActionResult的泛型對(duì)象。
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
今天的博客中,我們將一起看一下.NET Core Mvc是如何返回一個(gè)空值對(duì)象的,以及如何改變.NET Core Mvc針對(duì)空值對(duì)象結(jié)果的默認(rèn)行為。
下面話不多說了,來一起看看詳細(xì)的介紹吧
.NET Core Mvc針對(duì)空值對(duì)象的默認(rèn)處理行為
那么當(dāng)我們?cè)贏ction中返回null時(shí), 結(jié)果是什么樣的呢?
下面我們新建一個(gè)ASP.NET Core WebApi項(xiàng)目,并添加一個(gè)BookController, 其代碼如下:
[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
private readonly List<Book> _books = new List<Book> {
new Book(1, "CLR via C#"),
new Book(2, ".NET Core Programming")
};
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var item = _books.FirstOrDefault(p => p.BookId == id);
return Ok(item);
}
//[HttpGet("{id}")]
//public ActionResult<Book> GetById(int id)
//{
// var book = _books.FirstOrDefault(p => p.BookId == id);
// return book;
//}
//[HttpGet("{id}")]
//public Book GetById(int id)
//{
// var book = _books.FirstOrDefault(p => p.BookId == id);
// return book;
//}
}
public class Book
{
public Book(int bookId, string bookName)
{
BookId = bookId;
BookName = bookName;
}
public int BookId { get; set; }
public string BookName { get; set; }
}
在這個(gè)Controller中,我們定義了一個(gè)圖書的集合,并提供了根據(jù)圖書ID查詢圖書的三種實(shí)現(xiàn)方式。
然后我們啟動(dòng)項(xiàng)目, 并使用Postman, 并請(qǐng)求/api/book/3, 結(jié)果如下:

你會(huì)發(fā)現(xiàn)返回的Status是204 NoContent, 而不是我們想象中的200 OK。你可修改之前代碼的注釋, 使用其他2種方式,結(jié)果也是一樣的。
你可以嘗試創(chuàng)建一個(gè)普通的ASP.NET Mvc項(xiàng)目, 添加相似的代碼,結(jié)果如下

返回的結(jié)果是200 OK, 內(nèi)容是null
為什么會(huì)出現(xiàn)結(jié)果呢?
與前輩們(ASP.NET Mvc, ASP.NET WebApi)不同,ASP.NET Core Mvc非常巧妙的處理了null值,在以上的例子中,ASP.NET Core Mvc會(huì)選擇一個(gè)合適的輸出格式化器(output formatter)來輸出響應(yīng)內(nèi)容。通常這個(gè)輸出格式化器會(huì)是一個(gè)JSON格式化器或XML格式化器。
但是對(duì)于null值, ASP.NET Core Mvc使用了一種特殊的格式化器HttpNoContentOutputFormatter, 它會(huì)將null值轉(zhuǎn)換成204的狀態(tài)碼。這意味著null值不會(huì)被序列化成JSON或XML, 這可能不是我們期望的結(jié)果, 有時(shí)候我們希望返回200 OK, 響應(yīng)內(nèi)容為null。
Tips: 當(dāng)Action返回值是void或Task時(shí),ASP.NET Core Mvc默認(rèn)也會(huì)使用HttpNoContentOutputFormatter
通過修改配置移除默認(rèn)的null值格式化器
我們可以通過設(shè)置HttpNoContentOutputFormatter對(duì)象的TreatNullValueAsNoContent屬性為false,去除默認(rèn)的HttpNoContentOutputFormatter對(duì)null值的格式化。
在Startup.cs文件的ConfigureService方法中, 我們?cè)谔砑覯vc服務(wù)的地方,修改默認(rèn)的輸出格式化器,代碼如下
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.OutputFormatters.RemoveType(typeof(HttpNoContentOutputFormatter));
o.OutputFormatters.Insert(0, new HttpNoContentOutputFormatter
{
TreatNullValueAsNoContent = false;
});
});
}
修改之后我們重新運(yùn)行程序,并使用Postman訪問/api/book/3
結(jié)果如下, 返回值200 OK, 內(nèi)容為null, 這說明我們的修改成功了。

使用404 Not Found代替204 No Content
在上面的例子中, 我們禁用了204 No Content行為,響應(yīng)結(jié)果變?yōu)榱?00 OK, 內(nèi)容為null。 但是有時(shí)候,我們期望當(dāng)找不到任何結(jié)果時(shí),返回404 Not Found , 那么這時(shí)候我們應(yīng)該修改代碼,進(jìn)行擴(kuò)展呢?
在.NET Core Mvc中我們可以使用自定義過濾器(Custom Filter), 來改變這一行為。
這里我們創(chuàng)建2個(gè)特性類NotFoundActionFilterAttribute和NotFoundResultFilterAttribute , 代碼如下:
public class NotFoundActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is ObjectResult objectResult && objectResult.Value == null)
{
context.Result = new NotFoundResult();
}
}
}
public class NotFoundResultFilterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objectResult && objectResult.Value == null)
{
context.Result = new NotFoundResult();
}
}
}
代碼解釋
- 這里使用了ActionFilterAttribute和ResultFilterAttribute,ActionFilterAttribute中的OnActionExecuted方法會(huì)在action執(zhí)行完后觸發(fā), ResultFilterAttribute的OnResultExecuting會(huì)在action返回結(jié)果前觸發(fā)。
- 這2個(gè)方法都是針對(duì)action的返回結(jié)果進(jìn)行了替換操作,如果返回結(jié)果的值是null, 就將其替換成NotFoundResult
添加完成后,你可以任選一個(gè)類,將他們添加在
controller頭部
[Route("api/[controller]")]
[ApiController]
[NotFoundResultFilter]
public class BookController : ControllerBase
{
...
}
或者action頭部
[HttpGet("{id}")]
[NotFoundResultFilter]
public IActionResult GetById(int id)
{
var item = _books.FirstOrDefault(p => p.BookId == id);
return Ok(item);
}
你還可以在添加Mvc服務(wù)的時(shí)候配置他們
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.Filters.Add(new NotFoundResultFilterAttribute());
});
}
選擇一種重新運(yùn)行項(xiàng)目之后,效果和通過修改配置移除默認(rèn)的null值格式化器是一樣的。
IAlwaysRunResultFilter
以上的幾種解決方案看似完美無缺,但實(shí)際上還是存在一點(diǎn)瑕疵。由于ASP.NET Core Mvc中過濾器的短路機(jī)制(即在任何一個(gè)過濾器中對(duì)Result賦值都會(huì)導(dǎo)致程序跳過管道中剩余的過濾器),可能現(xiàn)在使用某些第三方組件后, 第三方組件在管道中插入自己的短路過濾器,從而導(dǎo)致我們的代碼失效。
ASP.NET Core Mvc的過濾器,可以參見這篇文章
下面我們添加以下的短路過濾器。
public class ShortCircuitingFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = new ObjectResult(null);
}
}
然后修改BookController中GetById的方法
[HttpGet("{id}")]
[ShortCircuitingFilter]
[NotFoundActionFilter]
public IActionResult GetById(int id)
{
var item = _books.FirstOrDefault(p => p.BookId == id);
return Ok(item);
}
重新運(yùn)行程序后,使用Postman訪問/api/book/3, 程序又返回了204 Not Content, 這說明我們的代碼失效了。
這時(shí)候,為了解決這個(gè)問題,我們需要使用.NET Core 2.1中新引入的接口IAlwaysRunResultFilter。實(shí)現(xiàn)IAlwaysRunResultFilter接口的過濾器總是會(huì)執(zhí)行,不論前面的過濾器是否觸發(fā)短路。
這里我們添加一個(gè)新的過濾器NotFoundAlwaysRunFilterAttribute。
public class NotFoundAlwaysRunFilterAttribute : Attribute, IAlwaysRunResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
}
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objectResult && objectResult.Value == null)
{
context.Result = new NotFoundResult();
}
}
}
然后我們繼續(xù)修改BookController中的GetById方法, 為其添加NotFoundAlwaysRunFilter特性
[HttpGet("{id}")]
[ShortCircuitingFilter]
[NotFoundActionFilter]
[NotFoundAlwaysRunFilter]
public IActionResult GetById(int id)
{
var item = _books.FirstOrDefault(p => p.BookId == id);
return Ok(item);
}
重新運(yùn)行程序后,使用Postman訪問/api/book/3, 程序又成功返回了404 Not Found, 這說明我們的代碼又生效了。
本篇源代碼: https://github.com/lamondlu/NullAction (本地下載)
原文地址:https://www.strathweb.com/2018/10/convert-null-valued-results-to-404-in-asp-net-core-mvc/
作者: Filip W.
譯者: Lamond Lu
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- ASP.NET Core MVC 過濾器的使用方法介紹
- asp.net core MVC 過濾器之ActionFilter過濾器(2)
- asp.net?core?MVC?全局過濾器之ExceptionFilter過濾器(1)
- ASP.NET Core MVC 修改視圖的默認(rèn)路徑及其實(shí)現(xiàn)原理解析
- asp.net core MVC之實(shí)現(xiàn)基于token的認(rèn)證
- ASP.NET Core MVC解決控制器同名Action請(qǐng)求不明確的問題
- 如何在Asp.Net Core MVC中處理null值的實(shí)現(xiàn)
- ASP.NET Core MVC如何實(shí)現(xiàn)運(yùn)行時(shí)動(dòng)態(tài)定義Controller類型
- ASP.NET Core MVC/WebApi基礎(chǔ)系列2
- ASP.NET Core MVC/WebApi基礎(chǔ)系列1
- 如何使用Rotativa在ASP.NET Core MVC中創(chuàng)建PDF詳解
- ASP.NET Core MVC 過濾器(Filter)
相關(guān)文章
ASP.NET2.0數(shù)據(jù)庫(kù)入門之SqlDataSource
ASP.NET2.0數(shù)據(jù)庫(kù)入門之SqlDataSource...2006-09-09
c# .net在WEB頁(yè)中的COOKIES設(shè)置技巧
c# .net在WEB頁(yè)中的COOKIES設(shè)置技巧,需要的朋友可以參考下。2011-07-07
.Net中如何將一個(gè)實(shí)例的內(nèi)存二進(jìn)制內(nèi)容讀出來(超簡(jiǎn)單方法)
這篇文章主要介紹了如何將一個(gè)實(shí)例的內(nèi)存二進(jìn)制內(nèi)容讀出來(超簡(jiǎn)單方法),接下來的內(nèi)容中,我們將利用一個(gè)簡(jiǎn)單的方法輸出指定實(shí)例的字節(jié)序列,并此次分析值類型和引用類型實(shí)例在內(nèi)存的布局,需要的朋友可以參考下2023-07-07
gridview中實(shí)現(xiàn)radiobutton的單選示例
radiobutton可以單選,于是想讓gridview也可以實(shí)現(xiàn),具體的思路及代碼如下,感興趣的朋友可以參考下2013-08-08
asp.net發(fā)布后web.config中compilation的debug的值true和false區(qū)別點(diǎn)整理
在本篇文章里小編給大家整理的是一篇關(guān)于asp.net發(fā)布后web.config中compilation的debug的值true和false區(qū)別點(diǎn)內(nèi)容,有需要的為朋友們可以參考下。2020-01-01

