Asp.net核心返回View或Json / XML的最简洁方法

时间:2018-10-08 15:25:33

标签: c# asp.net asp.net-core

在asp.net核心中,我想设置我的API控制器以执行以下操作:

默认为return View(model);

/api/id.json转换为return model;作为json

/api/id.xml转换为return model;作为xml

后两个可以通过使用[FormatFilter] see here

来实现
[FormatFilter]
public class ProductsController
{
    [Route("[controller]/[action]/{id}.{format?}")]
    public Product GetById(int id)

但是,这需要方法返回一个对象而不是View(object)。无论如何,还可以干净地支持还返回Views?

4 个答案:

答案 0 :(得分:4)

您不能同时执行两项操作。但是,您可以将通用功能分解为私有方法,然后以最少的代码重复实现两个操作:

[Route("[controller]")]
[FormatFilter]
public class ProductsController : Controller
{
    private Product GetByIdCore(int id)
    {
        // common code here, return product
    }

    [HttpGet("[action]/{id}")]
    [ActionName("GetById")]
    public IActionResult GetByIdView(int id) => View(GetByIdCore(id));

    [HttpGet("[action]/{id}.{format}")]
    public Product GetById(int id) => GetByIdCore(id);
}

此处必须使用不同的动作名称,因为方法签名不能仅在返回类型上有所不同。但是,可以使用[ActionName]属性,使它们看起来具有相同的名称,以用于生成URL等。

答案 1 :(得分:2)

您实际上可以只用一个动作就可以实现。这是我如何使其工作的示例:

[FormatFilter]
public class ProductsController : Controller
{
    [Route("[controller]/[action]/{id}.{format?}")]
    public IActionResult GetById(int id, string format)
    {
        var yourModel = ...;

        if (string.IsNullOrWhiteSpace(format))
            return View(yourModel);

        return Ok(yourModel);
    }

通过使用IActionResult作为返回类型,您可以返回ViewResultOkObjectResult。您可以通过将format值用作操作中的参数来访问它,检查它是否为空,然后做出相应的反应。

我还添加了Controller作为基类,以便访问用于创建相关结果的便捷方法(View(...)Ok(...))。

答案 2 :(得分:1)

如果要大量使用此模式,为使控制器尽可能整洁,可以创建一个公开“ FormatOrView”方法的基类:

[FormatFilter]
public abstract class FormatController : Controller
{
  protected ActionResult FormatOrView(object model)
  {
    var filter = HttpContext.RequestServices.GetRequiredService<FormatFilter>();
    if (filter.GetFormat(ControllerContext) == null)
    {
      return View(model);
    }
    else
    {
      return new ObjectResult(model);
    }
  }
}

然后您的控制器可以从中继承并使用FormatOrView方法

public class ProductsController : FormatController
{
  [Route("[controller]/[action]/{id}.{format?}")]
  public ActionResult GetById(int id)
  {
    var product = new { Id = id };

    return FormatOrView(product);
  }
}

编辑以列出GreyCloud最终接受的答案:这是一种通用的稍微简化的方法,您可以将其放入控制器中(或制作扩展方法,或放入上述的抽象基类中)。注意?。如果由于某种原因未定义服务。

private ActionResult<T> FormatOrView<T>(T model) {
     return HttpContext.RequestServices.GetRequiredService<FormatFilter>()?.GetFormat(ControllerContext) == null 
          ? View(model) 
          : new ActionResult<T>(model);

}

答案 3 :(得分:0)

FormatFilter是应用程序内容协商的一部分,在AspNetCore中,您可以控制您在具有更多控制权的ConfigureServices上处理输入或输出格式化程序,甚至可以在其中添加更多媒体类型。

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc(options =>
                {
                    options .OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
                    options .InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options ));

                    //more output formatters
                    var jsonOutputFormatter = options.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault();

                    if (jsonOutputFormatter != null)
                    {

   jsonOutputFormatter.SupportedMediaTypes.Add("application/vnd.myvendormediatype");                 

                    }
                }
}

但是回到控制器中的内容协商,您只能保留一个。唯一的事情是您需要知道mediaType才能返回View或json内容。仅确保传递具有所需内容类型的accept标头。使用您要为api或mvc应用程序定义的内容类型,这是客户端应期望的内容/格式

 [HttpGet("[action]/{id}")]
 public IActionResult public Product GetById(int id, [FromHeader(Name = "Accept")] string mediaType)
        {
           if (mediaType == "application/vnd.myvendormediatype")
           {
                 var data = GetYourData(...)
                 return Json(data);
           }
           else return View("YourDefaultView");
        }