Ядро Asp.net Самый чистый способ вернуть View или Json / XML - PullRequest
0 голосов
/ 08 октября 2018

В ядре 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 (объект).Есть ли в любом случае, чтобы чисто поддерживать также возвращение просмотров?

Ответы [ 4 ]

0 голосов
/ 08 октября 2018

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 голосов
/ 08 октября 2018

Вы можете достичь этого, просто используя одно действие.Вот пример того, как я заставил его работать:

[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 голосов
/ 08 октября 2018

Если вы собираетесь часто использовать этот шаблон, чтобы сохранить ваши контроллеры как можно более чистыми, вы можете создать базовый класс, который предоставляет метод «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);

}

0 голосов
/ 08 октября 2018

Вы не можете делать оба в одном действии.Однако вы можете выделить общие функциональные возможности в частный метод, а затем реализовать два действия с минимальным дублированием кода:

[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 и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...