(转)C# WebApi 接口返回值不困惑:返回值类型详解
原文地址:http://www.cnblogs.com/landeanfen/p/5501487.html
正文
前言:已经有一个月没写点什么了,感觉心里空落落的。今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧。之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解 ,这篇博文内容本身很基础,没想到引起很多园友关注,感谢大家的支持。作为程序猿,我们都知道参数和返回值是编程领域不可分割的两大块,此前分享了下WebApi的传参机制,今天再来看看WebApi里面另一个重要而又基础的知识点:返回值。还是那句话:本篇针对初初使用WebApi的同学们,比较基础,有兴趣的且看看。
WebApi系列文章
- C#进阶系列——WebApi接口测试工具:WebApiTestClient
- C#进阶系列——WebApi 跨域问题解决方案:CORS
- C#进阶系列——WebApi身份认证解决方案:Basic基础认证
- C#进阶系列——WebApi接口传参不再困惑:传参详解
- C#进阶系列——WebApi接口返回值不困惑:返回值类型详解
- C#进阶系列——WebApi异常处理解决方案
- C#进阶系列——WebApi区域Area使用小结
使用过Webapi的园友应该都知道,Webapi的接口返回值主要有四种类型
- void无返回值
- IHttpActionResult
- HttpResponseMessage
- 自定义类型
此篇就围绕这四块分别来看看它们的使用。
一、void无返回值
void关键字我们都不陌生,它申明方法没有返回值。它的使用也很简单,我们来看一个示例就能明白。

- public class ORDER
- {
- public string ID { get; set; }
- public string NO { get; set; }
- public string NAME { get; set; }
- public string DESC { get; set; }
- }


- public class OrderController : ApiController
- {
- [HttpPost]
- public void SaveOrder(ORDER name)
- {
- //处理业务逻辑
- }
- }

在Web里面调用

- $(function () {
- $.ajax({
- type: 'post',
- url: 'http://localhost:21528/api/Order/SaveOrder',
- data: { ID: "aaa", NAME: "test" },
- success: function (data, status) {
- alert(data);
- }
- });
- });

得到结果
可以看到,使用void申明的方法,在success方法里面得不到返回值,并且会返回http状态码204,告诉客户端此请求没有返回值。
二、IHttpActionResult
IHttpActionResult类型是WebApi里面非常重要的一种返回值类型。下面博主就根据平时在项目里面使用最多的几种方式来讲解下这种类型的返回值的一些用法。
1、Json<T>(T content)
使用MVC开发过的朋友一定记得,在MVC里面,请求数据的接口的返回值类型大部分使用的是JsonResult,在MVC里面你一定也写过类似这样的接口:
- public JsonResult GetResult()
- {
- return Json(new { }, JsonRequestBehavior.AllowGet);
- }
那么,在WebAPI里面是否也存在类似的用法呢。呵呵,在这点上面,微软总是贴心的。在WebApi的ApiController这个抽象类里面,为我们封装了Json<T>(T content)这个方法,它的用法和MVC里面的JsonResult基本类似。我们通过一个例子来说明它的用法:

- [HttpGet]
- public IHttpActionResult GetOrder()
- {
- var lstRes = new List<ORDER>();
- //实际项目中,通过后台取到集合赋值给lstRes变量。这里只是测试。
- lstRes.Add(new ORDER() { ID = "aaaa", NO = "111", NAME = "111", DESC = "1111" });
- lstRes.Add(new ORDER() { ID = "bbbb", NO = "222", NAME = "222", DESC = "2222" });
- return Json<List<ORDER>>(lstRes);
- }

看到这个代码,有人就疑惑了,我们定义的返回值类型是IHttpActionResult类型,直接返回Json<T>(T content)这样可行么?我们将Json转到定义看看:
- protected internal JsonResult<T> Json<T>(T content);
我们继续将JsonResult<T>转到定义
原来JsonResult<T>是实现了IHttpActionResult接口的,难怪可以直接返回呢。
知道了这个,我们直接在Web里面通过ajax请求来调用:

- $(function () {
- $.ajax({
- type: 'get',
- url: 'http://localhost:21528/api/Order/GetOrder',
- data: {},
- success: function (data, status) {
- alert(data);
- }
- });
- });

来看结果:
既然实体类可以直接这样传递,那么如果我们想要传递一些匿名类型呢,因为很多情况下,我们需要返回到前端的对象都没有对应的实体来对应,如果我们想要返回匿名对象怎么办呢?我们知道,这里的Json<T>(T content)必须要传一个对应的泛型类型,如果是匿名类型这里肯定不好传。还好有我们的object类型,当然你可以使用dynamic,我们来试一把。

- [HttpGet]
- public IHttpActionResult GetOrder()
- {
- return Json<dynamic>(new { AA = "", BB = "cc" });
- }

同样的来看测试结果:
2、Ok()、 Ok<T>(T content)
除了Json<T>(T content),在ApiController里面还有另外一个比较常用的方法:Ok()。同样,我们将Ok()转到定义
- protected internal virtual OkResult Ok();
OkResult转到定义
有了这个作为基础,我们就可以放心大胆的使用了。

- [HttpGet]
- public IHttpActionResult GetOKResult()
- {
- return Ok();
- }

得到结果
如果返回Ok(),就表示不向客户端返回任何信息,只告诉客户端请求成功。
除了Ok()之外,还有另外一个重载Ok<T>(T content)。

- [HttpGet]
- public IHttpActionResult GetOKResult(string name)
- {
- return Ok<string>(name);
- }

这种用法和Json<T>(T content)比较类似,如果你非要问这两者有什么区别,或者说怎么选择两者。那么我的理解是如果是返回实体或者实体集合,建议使用Json<T>(T content),如果是返回基础类型(如int、string等),使用Ok<T>(T content)。
3、NotFound()
当需要向客户端返回找不到记录时,有时需要用到NotFound()方法。
- protected internal virtual NotFoundResult NotFound();
来看看它的使用场景

- [HttpGet]
- public IHttpActionResult GetNotFoundResult(string id)
- {
- var lstRes = new List<ORDER>();
- //实际项目中,通过后台取到集合赋值给lstRes变量。这里只是测试。
- lstRes.Add(new ORDER() { ID = "aaaa", NO = "111", NAME = "111", DESC = "1111" });
- lstRes.Add(new ORDER() { ID = "bbbb", NO = "222", NAME = "222", DESC = "2222" });
- var oFind = lstRes.FirstOrDefault(x => x.ID == id) ;
- if (oFind == null)
- {
- return NotFound();
- }
- else
- {
- return Json<ORDER>(oFind);
- }
- }


- $(function () {
- $.ajax({
- type: 'get',
- url: 'http://localhost:21528/api/Order/GetNotFoundResult',
- data: { id :"cccc" },
- success: function (data, status) {
- alert(data);
- }
- });
- });

得到结果
NotFound()方法会返回一个404的错误到客户端。
4、其他
其他还有一些方法,都有它特定的用途。在此贴出来。
4.1、Content<T>(HttpStatusCode statusCode, T value)

- [HttpGet]
- public IHttpActionResult GetContentResult()
- {
- return Content<string>(HttpStatusCode.OK, "OK");
- }

向客户端返回值和http状态码。
4.2、BadRequest()

- [HttpGet]
- public IHttpActionResult GetBadRequest(ORDER order)
- {
- if (string.IsNullOrEmpty(order.ID))
- return BadRequest();
- return Ok();
- }

向客户端返回400的http错误。
4.3、Redirect(string location)

- [HttpGet]
- public IHttpActionResult RedirectResult()
- {
- return Redirect("http://localhost:21528/api/Order/GetContentResult");
- }

将请求重定向到其他地方。
5、自定义IHttpActionResult接口的实现
上面介绍了一些系统内置的常用的实现IHttpActionResult接口的方法。如果我们需要自定义IHttpActionResult的返回呢?
在介绍之前,我们有必要先来看看IHttpActionResult类型的定义,将IHttpActionResult转到定义可以看到:

- namespace System.Web.Http
- {
- // 摘要:
- // Defines a command that asynchronously creates an System.Net.Http.HttpResponseMessage.
- public interface IHttpActionResult
- {
- // 摘要:
- // Creates an System.Net.Http.HttpResponseMessage asynchronously.
- //
- // 参数:
- // cancellationToken:
- // The token to monitor for cancellation requests.
- //
- // 返回结果:
- // A task that, when completed, contains the System.Net.Http.HttpResponseMessage.
- Task<System.Net.Http.HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken);
- }
- }

这个接口包含唯一的一个方法ExecuteAsync(),此方法将以异步方式创建一个HttpResponseMessage实例返回给客户端。
有了这个作为基础,下面,我们自定义一个bootstrapTable服务端分页的子类去展示自定义IHttpActionResult的用法。
首先,自定义一个实现类

- public class PageResult : IHttpActionResult
- {
- object _value;
- HttpRequestMessage _request;
- public PageResult(object value, HttpRequestMessage request)
- {
- _value = value;
- _request = request;
- }
- public Task<HttpResponseMessage> ExecuteAsync(System.Threading.CancellationToken cancellationToken)
- {
- var response = new HttpResponseMessage()
- {
- Content = new ObjectContent(typeof(object), _value, new JsonMediaTypeFormatter()),
- RequestMessage = _request
- };
- return Task.FromResult(response);
- }
- }

然后,在API接口里面返回PageResult对象

- [HttpGet]
- public IHttpActionResult GetPageRow(int limit, int offset)
- {
- var lstRes = new List<ORDER>();
- //实际项目中,通过后台取到集合赋值给lstRes变量。这里只是测试。
- lstRes.Add(new ORDER() { ID = "aaaa", NO = "111", NAME = "111", DESC = "1111" });
- lstRes.Add(new ORDER() { ID = "bbbb", NO = "222", NAME = "222", DESC = "2222" });
- var oData = new { total = lstRes.Count, rows = lstRes.Skip(offset).Take(limit).ToList() };
- return new PageResult(oData, Request);
- }

最好,ajax调用

- $(function () {
- $.ajax({
- type: 'get',
- url: 'http://localhost:21528/api/Order/GetPageRow',
- data: { limit:1,offset:1},
- success: function (data, status) {
- alert(data);
- }
- });
- });

得到结果
三、HttpResponseMessage
在上文自定义IHttpActionResult返回类型的时候,提到过HttpResponseMessage这个对象。它表示向客户端返回一个http响应的消息对象(包含http状态码和需要返回客户端的消息)。这个对象也有它独特的使用场景:需要向客户端返回HttpResponse时就要用到这个对象。以导出为例,由于需要将导出的Excel文件输出到客户端浏览器,Webapi的服务端需要向Web的客户端输出文件流,这个时候一般的IHttpActionResult对象不方便解决这个问题,于是HttpReponseMessage派上了用场。我们来看看它的使用示例。

- public HttpResponseMessage Export()
- {
- //取数据
- var lstRes = OrderBLL.Export();
- //向Excel里面填充数据
- HSSFWorkbook workbook = new HSSFWorkbook();
- CreateAndFillSheet(workbook, lstRes);
- //保存到服务
- var fileName = "Excel" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls";
- var strPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Data\" + fileName);
- using (FileStream fs = new FileStream(strPath, FileMode.Create))
- {
- workbook.Write(fs);
- using (MemoryStream ms = new MemoryStream())
- {
- workbook.Write(ms);
- }
- }
- //输出到浏览器
- try
- {
- var stream = new FileStream(strPath, FileMode.Open);
- HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
- response.Content = new StreamContent(stream);
- response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
- response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
- {
- FileName = fileName
- };
- return response;
- }
- catch
- {
- return new HttpResponseMessage(HttpStatusCode.NoContent);
- }
- }

将文件流保存在StreamContent对象里面,然后输出到浏览器。在浏览器端即可将Excel输出。
四、自定义类型
以上几种返回值类型能解决我们大部分返回值的问题,当然,你也可以将webapi的接口和普通方法一样,返回任意的类型,WebApi会自动序列化你自定义任何返回类型,然后将序列化的值写到响应正文里,状态码统一返回200。比如:

- [HttpGet]
- public object GetOther()
- {
- var lstRes = new List<ORDER>();
- //实际项目中,通过后台取到集合赋值给lstRes变量。这里只是测试。
- lstRes.Add(new ORDER() { ID = "aaaa", NO = "111", NAME = "111", DESC = "1111" });
- lstRes.Add(new ORDER() { ID = "bbbb", NO = "222", NAME = "222", DESC = "2222" });
- return lstRes;
- }

得到结果
和上面的Json、Ok等用法在效果上面没有太大区别。
(转)C# WebApi 接口返回值不困惑:返回值类型详解的更多相关文章
- C#进阶系列——WebApi接口传参不再困惑:传参详解(转载)
原文地址: http://www.cnblogs.com/landeanfen/p/5337072.html 前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用 ...
- WebApi接口传参不再困惑:传参详解
http://www.cnblogs.com/landeanfen/p/5337072.html
- C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解 ...
- [转]C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
本文转自:http://www.cnblogs.com/landeanfen/p/5501487.html 阅读目录 一.void无返回值 二.IHttpActionResult 1.Json(T c ...
- C#进阶系列——WebApi 接口参数不再困惑:传参详解
前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...
- WebApi 接口参数不再困惑:传参详解
阅读目录 一.get请求 1.基础类型参数 2.实体作为参数 3.数组作为参数 4.“怪异”的get请求 二.post请求 1.基础类型参数 2.实体作为参数 3.数组作为参数 4.后台发送请求参数的 ...
- 【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
原文地址:http://www.cnblogs.com/landeanfen/archive/2016/04/06/5337072.html 阅读目录 一.get请求 1.基础类型参数 2.实体作为参 ...
- C#WebApi 接口参数不再困惑:传参详解
前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...
- [转]C#进阶系列——WebApi 接口参数不再困惑:传参详解
本文转自:http://www.cnblogs.com/landeanfen/p/5337072.html#_label1_2 阅读目录 一.get请求 1.基础类型参数 2.实体作为参数 3.数组作 ...
随机推荐
- mysql存储程序
什么时候会用到存储过程 1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度2.当对数据库进行复杂操作时 ...
- React Native 学习资料
React Native 学习资料 学习资料 网址 React Native中文网 https://reactnative.cn/
- 高级openg 混合,一个完整程序
1.当片段着色器处理完一个片段之后,模板测试(stencil test)会开始执行,和深度测试一样,它也可能会丢弃片段,接下来,被保留的片段会进入深度测试2.每个窗口库都需要为你配置一个模板缓冲,但是 ...
- learnopengl“入门”的全部代码
projection 里的角度,角度越大,物体离摄像机越远:角度越小,物体离摄像机越近,这个角度表示视野(fov),视野越大,看到的物体就越小 头文件 #ifndef SHADER_H_INCLUDE ...
- Linux 下的三种时间介绍
Linux 下的三种时间介绍: Access Time:简写为atime,表示文件访问的时间,当文件内容被访问时,更新atime时间 Modify Time:简写为mtime,表示文件内容修改的时间, ...
- STL 的 vector 根据元素的值来删除元素的方法
vector 的 erase( ) 只能删除迭代器,所以要想删某种值的元素,需要这样: 假设有一个 vector 叫 vt ,则 vt.erase( remove( vt.begin() , vt.e ...
- centos7 使用cgroup进行资源限制
centos7中进行资源限制,使用的仍然是cgroup,只是配置接口使用的systemd. 下文将介绍如何使用systemd进行资源限制. Step1 编写unit文件 命令为my-demo.serv ...
- Hanlp自然语言处理工具的使用演练
Hanlp是由一系列模型与算法组成的工具包,目标是普及自然语言处理在生产环境中的应用.Hanlp具备功能完善.性能高效.架构清洗.语料时新.可自定义的特点:提供词法分析(中文分词.磁性标注.命名实体识 ...
- SDRAM的初始化与刷新操作---看时序图写代码
SDRAM的初始化与刷新操作---看时序图写代码 1.SDRAM的常见操作 2.初始化就是配置SDRAM 3.SDRAM初始化时序 时序解释如下: 4.刷新操作
- 单进程与 多进程关系及区别(多进程系统linux)
单进程编程:顺序执行 数据同步 复杂度低 用途单一 多进程编程:同时执行 数据异步 复杂度高 用途广泛 1. 多进程的优势在于任务的独立性,比如某个任务单独作为一个进程的话,崩溃只影响自己的服务,其他 ...