ASP.NET Core Mvc中空返回值的处理方式
原文:
ASP.NET Core Mvc中空返回值的处理方式
.NET Core MVC在如何返回操作结果方面非常灵活的。 [HttpGet] public IActionResult SayGood() { return Content("Good!"); } 当然你还可以直接返回一个类的实例。 [HttpGet] public string HelloWorld() { return "Hello World"; } 在.NET Core 2.1中,你还可以返回一个 [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1","value2" }; } 今天的博客中,我们将一起看一下.NET Core Mvc是如何返回一个空值对象的,以及如何改变.NET Core Mvc针对空值对象结果的默认行为。 .NET Core Mvc针对空值对象的默认处理行为那么当我们在Action中返回null时,结果是什么样的呢? [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; } } 在这个Controller中,我们定义了一个图书的集合,并提供了根据图书ID查询图书的三种实现方式。 然后我们启动项目,并使用Postman,并请求/api/book/3,结果如下: 你会发现返回的Status是204 NoContent,而不是我们想象中的200 OK。你可修改之前代码的注释,使用其他2种方式,结果也是一样的。 你可以尝试创建一个普通的ASP.NET Mvc项目,添加相似的代码,结果如下 返回的结果是200 OK,内容是null 为什么会出现结果呢? 但是对于null值,ASP.NET Core Mvc使用了一种特殊的格式化器
通过修改配置移除默认的null值格式化器我们可以通过设置 在Startup.cs文件的 public void ConfigureServices(IServiceCollection services) { services.AddMvc(o => { o.OutputFormatters.RemoveType(typeof(HttpNoContentOutputFormatter)); o.OutputFormatters.Insert(0,new HttpNoContentOutputFormatter { TreatNullValueAsNoContent = false; }); }); } 修改之后我们重新运行程序,并使用Postman访问/api/book/3 结果如下,返回值200 OK,内容为null,这说明我们的修改成功了。 使用404 Not Found代替204 No Content在上面的例子中, 我们禁用了204 No Content行为,响应结果变为了200 OK,内容为null。 但是有时候,我们期望当找不到任何结果时,返回404 Not Found,那么这时候我们应该修改代码,进行扩展呢? 在.NET Core Mvc中我们可以使用自定义过滤器(Custom Filter),来改变这一行为。 这里我们创建2个特性类 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(); } } } 代码解释
添加完成后,你可以任选一个类,将他们添加在 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服务的时候配置他们 public void ConfigureServices(IServiceCollection services) { services.AddMvc(o => { o.Filters.Add(new NotFoundResultFilterAttribute()); }); } 选择一种重新运行项目之后,效果和通过修改配置移除默认的null值格式化器是一样的。 IAlwaysRunResultFilter以上的几种解决方案看似完美无缺,但实际上还是存在一点瑕疵。由于ASP.NET Core Mvc中过滤器的短路机制(即在任何一个过滤器中对Result赋值都会导致程序跳过管道中剩余的过滤器),可能现在使用某些第三方组件后,第三方组件在管道中插入自己的短路过滤器,从而导致我们的代码失效。
下面我们添加以下的短路过滤器。 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); } 重新运行程序后,使用Postman访问/api/book/3,程序又返回了204 Not Content,这说明我们的代码失效了。 这时候,为了解决这个问题,我们需要使用.NET Core 2.1中新引入的接口 这里我们添加一个新的过滤器 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(); } } } 然后我们继续修改BookController中的GetById方法,为其添加 [HttpGet("{id}")] [ShortCircuitingFilter] [NotFoundActionFilter] [NotFoundAlwaysRunFilter] public IActionResult GetById(int id) { var item = _books.FirstOrDefault(p => p.BookId == id); return Ok(item); } 重新运行程序后,使用Postman访问/api/book/3,程序又成功返回了404 Not Found,这说明我们的代码又生效了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc-3 – 无法使用Enity Framework 4.0设
- asp.net-mvc – TempData keep()vs peek()
- asp.net – 错误:类型存在于两个目录中
- asp.net-web-api – 带有OWIN OAuth承载令牌的We
- asp.net-mvc-3 – 对MVC4 Web API性能问题进行故
- asp.net mvc客户端验证
- 存储过程 – 经典ASP – ADO执行存储过程传入??参
- asp.net-mvc-3 – ASP.Net MVC 3多个CheckBoxLis
- ASP.NET MVC4 设置路由的命名空间 namespace
- .net – 如何在同一个域但不同的主机上共享本地存