在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?
答案 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
作为返回类型,您可以返回ViewResult
或OkObjectResult
。您可以通过将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");
}