Создание API на сайтах ASP.NET MVC - PullRequest
2 голосов
/ 18 ноября 2011

У меня есть веб-сайт ASP.NET MVC с такими действиями:

public ActionResult Show(int id)
{
    var customer = GetCustomer(id);

    return View(new ShowCustomerModel(customer));
}

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

public ActionResult Get(int id)
{
    var customer = GetCustomer(id);

    return Json(new CustomerResource(customer));
}

Вопрос в том, какие существуют инструменты или шаблоны ASP.NET MVC, которые позволяют мне объединять их вместе - например, Rails позволяет мне указывать несколько форматов возврата:

def index
  @customer = get_customer(...)

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @customer}
    format.json { render :json => @customer}
  end
end

Это даже хороший шаблон? Должен ли я просто иметь:

public Customer Get(int id)
{
    return GetCustomer(id);
}

И использовать фильтры действий для выборочной визуализации как JSON или представления?

Ответы [ 5 ]

8 голосов
/ 18 ноября 2011

Я бы создал ActionResult, который достаточно умен, чтобы определить, какой результат вы хотите получить на основе предоставленных Accept Headers , например:

public class AdaptiveActionResult : ActionResult
{
    private readonly object _model;

    public AdaptiveActionResult(object model)
    {
        _model = model;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var accept = context.HttpContext.Request.AcceptTypes;
        if (accept == null || !accept.Contains("application/json"))
        {
            var viewResult = new ViewResult {ViewData = new ViewDataDictionary(_model)};
            viewResult.ExecuteResult(context);
            return;
        }

        var jsonResult = new JsonResult
                             {
                                 Data = _model, 
                                 JsonRequestBehavior = JsonRequestBehavior.AllowGet
                             };
        jsonResult.ExecuteResult(context);
        return;
    }
}

Это можно расширить, чтобы проверить, хотят ли они XML, RSS, Atom, что угодно.

Тогда вы можете сделать это в вашем контроллере следующим образом:

    public ActionResult Index()
    {
        return new AdaptiveActionResult(new MyModel());
    }
6 голосов
/ 18 ноября 2011

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

public class ContentTypeRequestFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.ActionParameters["contentType"] = 
            filterContext.HttpContext.Request.ContentType;
        base.OnActionExecuting(filterContext);
    }
}  

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

[ContentTypeRequestFilter]
public ActionResult Get(int id, string contentType)
{
    var customer = GetCustomer(id);
    if(contentType.ToLower() == "application/json")
    {
        return Json(new CustomerResource(customer));
    }
    return View(new ShowCustomerModel(customer));
}  

Оттуда вы можете адаптироваться к другим типам запросов контента (xml и т. д.), если это необходимо.Я применил аналогичный подход к своему сообщению в блоге о Построение архитектуры REST API в MVC 3 .

3 голосов
/ 18 ноября 2011

@ Schwarty's answer - лучший вариант, если вы хотите придерживаться своего сайта и делать автоматическую рубиновую вещь.

Но я бы посоветовал вам создать отдельный интерфейс дляAPI.Основная проблема - управление версиями - вам действительно не следует публиковать не версии API.Мы, люди, читающие веб-страницы, можем легко приспособиться к обновленной модели данных.Возможно, не легко, но мы обычно не ломаемся, когда что-то меняется.Однако потребителями вашего API являются машины, которые часто привязаны к вашей конкретной реализации и не любят изменений.

Краткая версия: они являются отдельными задачами и в большинстве случаев должны обрабатываться отдельно.

3 голосов
/ 18 ноября 2011

Это может быть не совсем то, что вы хотите, но вы можете проверить тип запроса и действовать соответственно.

public ActionResult Get(int id)
{
   if (Request.IsAjaxRequest)
   {
      // do stuff
      return new JsonResult(new { Foo = "Foo", Bar = "Bar" });
   }
   else
   {
      // do stuff
      return View(myModel);
   }

   // if you need beyond IsAjaxRequest, you could
   // check the controller's Request object for other 
   // indicators as to what type of result to
   // send back
}
1 голос
/ 18 ноября 2011

Вы можете иметь 3 действия для каждого, создать собственный атрибут и создать ActionInvoker точно так же, как работают HttpPost и HttpGet

[JsonRequest]
[ActionName("Get")]
public ActionResult GetAsJson(int id) {
    //return as Json
}

[XmlRequest]
[ActionName("Get")]
public ActionResult GetAsXml(int id) {
    //return as xml
}

public ActionResult Get(int id) {
    //get stuff normally
}

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

Другим способом было бы создать объект CustomerResult и просто выполнить одно действие и выполнить действия, указанные Даниэлем, но эту логику можно вставить в ваш объект CustomerResult.

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