C#進階系列——WebApi 接口返回值不困惑:返回值類型詳解

前言:已經有一個月沒寫點什麼了,感受內心空落落的。今天再來篇乾貨,想要學習Webapi的園友們速速動起來,跟着博主一塊兒來學習吧。以前分享過一篇 C#進階系列——WebApi接口傳參再也不困惑:傳參詳解 ,這篇博文內容自己很基礎,沒想到引發不少園友關注,感謝你們的支持。做爲程序猿,咱們都知道參數和返回值是編程領域不可分割的兩大塊,此前分享了下WebApi的傳參機制,今天再來看看WebApi裏面另外一個重要而又基礎的知識點:返回值。仍是那句話:本篇針對初初使用WebApi的同窗們,比較基礎,有興趣的且看看。html

WebApi系列文章前端

使用過Webapi的園友應該都知道,Webapi的接口返回值主要有四種類型web

  • void無返回值
  • IHttpActionResult
  • HttpResponseMessage
  • 自定義類型

此篇就圍繞這四塊分別來看看它們的使用。ajax

1、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裏面調用bootstrap

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

獲得結果api

 

 能夠看到,使用void申明的方法,在success方法裏面得不到返回值,而且會返回http狀態碼204,告訴客戶端此請求沒有返回值。跨域

2、IHttpActionResult

IHttpActionResult類型是WebApi裏面很是重要的一種返回值類型。下面博主就根據平時在項目裏面使用最多的幾種方式來說解下這種類型的返回值的一些用法。瀏覽器

一、Json<T>(T content)

使用MVC開發過的朋友必定記得,在MVC裏面,請求數據的接口的返回值類型大部分使用的是JsonResult,在MVC裏面你必定也寫過相似這樣的接口:app

     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" });
        }

一樣的來看測試結果:

二、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)。

三、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.一、Content<T>(HttpStatusCode statusCode, T value)

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

向客戶端返回值和http狀態碼。

4.二、BadRequest()

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

向客戶端返回400的http錯誤。

4.三、Redirect(string location)

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

將請求重定向到其餘地方。

五、自定義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);
        }
    });
});

獲得結果

3、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輸出。

4、自定義類型

以上幾種返回值類型能解決咱們大部分返回值的問題,固然,你也能夠將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等用法在效果上面沒有太大區別。

5、總結 

以上經過四個方面詳細分享了下WebApi裏面返回值的常見用法,不能說哪一種方式最好,由於每種方式都有其特定的使用場景。博主以爲爲了規範WebApi接口,對於通常接口的返回值,儘可能使用IHttpActionResult類型做爲返回值,畢竟是微軟內置的東西,可能爲咱們考慮了不少咱們考慮不到的東西。固然,你可能會以爲麻煩,你可能會說直接和普通方法同樣來使用不是更爽,博主當初也有這種想法,但是學習微軟的東西多了以後發現不少東西仍是遵照必定的標準比較好,至少維護起來方便。這就像博主最近正在努力學習的WebApi+oData同樣,爲何要搞這麼一套標準性的東西,還不是爲了更加方便地規範Restful風格。若是本文能幫到你,不妨推薦下,您的推薦是博主繼續總結的動力!

相關文章
相關標籤/搜索