Ядро Asp.net Самый чистый способ вернуть View или Json / XML

2

В ядре asp.net я хотел бы настроить свой контроллер API, чтобы сделать следующее:

по умолчанию return View(model);

/api/id.json return model; как json

/api/id.xml return model; как xml

Второе два может быть достигнуто с помощью [FormatFilter] см. Здесь

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

Однако для этого требуется, чтобы метод возвращал объект, а не объект View (объект). Есть ли все-таки, чтобы чисто поддерживать и возвращать Views?

Теги:
asp.net-core

4 ответа

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

}

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 и т.д.

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(...)).

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");
        }
  • 0
    это бы сработало, но это накладывает тяжелое бремя на потребителей API
  • 0
    Любой, кто использует API, должен знать согласование контента, которое вы выставляете. Вы можете документировать свой API с помощью Swagger, если хотите, но в конце концов это контракт между сервером и клиентом (производителем / потребителем). Подумайте также о вашем подходе, вы заставляете их передавать параметр формата, чтобы сделать свое различие в бэкэнде

Ещё вопросы

Сообщество Overcoder
Наверх
Меню