2015-05-01 62 views
0

我正在使用ASP.NET MVC 5和/或WebAPI原型系統。我們需要創建可根據客戶端協商的內容類型以不同方式返回結果的服務。標準ASP.NET WebAPI方式返回協商文本/ html響應(剃刀)

例如如果客戶端請求text/html,我們希望返回使用Razor呈現的普通HTML網頁,但是如果客戶端請求JSON,我們應該返回一個寧靜的資源表示。

我看到有一個webapi contrib項目似乎支持這種情況(https://github.com/WebApiContrib/WebApiContrib.Formatting.Razor),但我想知道:有沒有一個標準的ASP.NET開箱即用的方式做到這一點?

回答

0

這在標準的MVC中應該很容易。考慮一個控制器動作:

public ActionResult SomeAction(SomeModel request) 
{ 
    // based on the request, return a response 
} 

這個動作可以返回任何ActionResult,無所謂哪一個。因此,在一個代碼路徑,你可以有:

return View(someViewModel); 

而在另一個你可能有:

return Json(someViewModel); 

第一個將控制權交給它將使用剃刀,並返回一個網頁正常視圖,第二個只會返回帶有序列化數據的JSON響應。

您如何確定代碼路徑取決於您,但正如您所說,這可以通過基於頭的內容協商完成。您可以可以直接在控制器中檢查標題,但是可能會將控制器耦合到對測試不太好的HTTP上下文。

如果會出現很多這樣的動作,或許一個優雅的方法是將自定義模型綁定器添加到MVC管道中,該管道本身檢查頭信息並將其傳遞給控制器​​。

所以,你的控制器動作(或多個)可以簡單地得到另一個參數:

public ActionResult SomeAction(HeaderInfo headers, SomeModel request) 
{ 
    // based on the values in "headers", return a response 
} 

HeaderInfo可以只是一個簡單的自定義類,它甚至可能只是一個布爾它對於初學者:

public class HeaderInfo 
{ 
    public bool ReturnDataOnly { get; set; } 
} 

基於該布爾的狀態,控制器返回整個視圖或僅返回數據(JSON)。然後,您可以將自定義模型綁定器注入到管道中,以自動將此綁定到包含它的所有請求。這種粘合劑可能是如此簡單:

public class HeaderInfoBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (controllerContext == null) 
      throw new ArgumentNullException("controllerContext"); 
     if (bindingContext == null) 
      throw new ArgumentNullException("bindingContext"); 

     var result = new HeaderInfo(); 
     // examine the contexts for headers, based on those headers 
     // set true or false on the object's property 

     return result; 
    } 
} 

然後將其添加到MVC配置bootstrappers(通常在App_Start):

public class BinderConfig 
{ 
    public static void RegisterBinders(ModelBinderDictionary binders) 
    { 
     binders[typeof(HeaderInfo)] = new HeaderInfoBinder(); 
    } 
} 

並調用從應用開始在Global.asax.cs這個配置:

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
RouteConfig.RegisterRoutes(RouteTable.Routes); 
BundleConfig.RegisterBundles(BundleTable.Bundles); 
BinderConfig.RegisterBinders(ModelBinders.Binders); // <-- right there 

經過這些少許調整,應用程序中任何具有HeaderInfo參數的控制器動作都會自動根據該HeaderInfoBinder中的邏輯,在運行時填充該參數,您可以根據自己的情況對其進行調整和添加。然後,任何控制器操作都可以檢查該對象以確定返回的內容。 (這可能會導致大量重複的switch塊,您可能會進一步重構爲某個通用幫助程序。)

+0

哇!很好的答案。如果我切換到MVC並不意味着我失去了例如IContentNegotiator,基於動詞的路由約定,OAuth認證等WebAPI的好處?從概念上講,我們認爲這是一個API,而不是一個「web應用程序」(在這個詞的普通asp.net意義上)。 – goofballLogic

+0

@goofballLogic:WebAPI中存在類似的功能。例如,對於自定義聯編程序,您可以編寫類似的東西(不同的基類和不同的配置引導程序),並使用參數屬性告訴WebAPI方法使用該聯編程序。 *假設*新版本(下一版本)以更加優雅的方式結合了MVC和WebAPI,而傳統上兩者雖然有些相似但框架管道相當類似。我不確定這是否意味着WebAPI很容易返回視圖,我從來沒有這樣做過。所有的概念應該是相同的。 – David