标准ASP.NET WebAPI方式返回协商文本/ html响应(Razor)

时间:2015-05-01 17:04:39

标签: asp.net razor asp.net-web-api

我正在使用ASP.NET MVC 5和/或WebAPI对系统进行原型设计。我们需要创建可以根据客户端协商的内容类型以不同方式返回结果的服务。

e.g。如果客户端请求text / html,我们想要返回使用Razor呈现的普通HTML网页,但如果客户端请求JSON,我们应该返回一个宁静的资源表示。

我看到有一个webapi contrib项目似乎支持这种情况(https://github.com/WebApiContrib/WebApiContrib.Formatting.Razor),但我想知道:是否有一种标准的ASP.NET开箱即用方式?< / p>

1 个答案:

答案 0 :(得分:0)

这在标准MVC中应该非常简单。考虑控制器操作:

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

该操作可以返回任何ActionResult,无论哪一个都无关紧要。所以在一个代码路径中你可能有:

return View(someViewModel);

而在另一个你可能有:

return Json(someViewModel);

第一个将控件传递给视图,该视图将使用Razor并正常返回网页,第二个将返回带有序列化数据的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块,您可以在某处进一步重构为通用助手。)