Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

REST aplikace pomocí ASP.NET MVC

11.20 - 26. října 2008 | ASP.NET 2.0

Jednou z důležitých novinek v beta verzi ASP.NET MVC je bezesporu možnost přetěžování akcí a ve spolupráci s filtry na typ požadavku můžeme přejít na REST přístup k architektuře webové aplikace.

Ačkoli si mnoho lidí myslí, že když píšou v ASP.NET MVC, mají rovnou REST out of the box, není tomu tak. V plném RESTu se totiž nedostávají do URL slovesa (akce), nýbrž pouze předměty. Typický scénář CRUD operací může vypadat následovně:

POST /Customers/Create/
GET /Customers/Detail/1
POST /Customers/Update/1
GET /Customers/Delete/1

Takovéto akce snadno pokryjeme s výchozí routou /{controller}/{action}/{id} a řadič CustomersController může vypadat nějak tak:

public class CustomersController : Controller {
  public ActionResult Create(Customer customer) {
    // TODO: create customer
  }

  public ActionResult Detail(int id) {
    // TODO: find customer by id
  }

  public ActionResult Update(int id, Customer customer) {
    // TODO: update customer
  }

  public ActionResult Delete(int id) {
    // TODO: delete customer
  }
}

Jenže POSTování a GETování je jen slabou podmnožinou povolených operací na HTTP protokolu. Správný REST přístup pro CRUD operace by měl vypadat následovně:

POST /Customers
GET /Customers/1
PUT /Customers/1
DELETE /Customers/1

Co je pro to třeba udělat?

Začneme od routy. Přidáme novou routu pro CustomersController:

routes.MapRoute(
  "REST Customers",
  "Customers/{id}",
  new { controller = "Customers", action = "Rest", id = "" }
);

A původní kód obohatíme o pár atributů:

public class CustomersController : Controller {

  [ActionName("Rest")]
  [HttpPost]
  public ActionResult Create(Customer customer) {
    // TODO: save Customer to database
    return new HttpStatusCodeResult((int)HttpStatusCode.OK);
  }

  [ActionName("Rest")]
  [HttpGet]
  public ActionResult Detail(int id) {
    // TODO: load Customer from database
    return View("Detail");
  }

  [ActionName("Rest")]
  [HttpGet]
  public ActionResult Index() {
    // TODO: load Customers from database
    return View("Index");
  }

  [ActionName("Rest")]
  [HttpPut]
  public ActionResult Update(int id, Customer customer) {
    // TODO: update Customer in database
    return new HttpStatusCodeResult((int)HttpStatusCode.OK);
  }

  [ActionName("Rest")]
  [HttpDelete]
  public ActionResult Delete(int id) {
    // TODO: delete Customer from database
   return new HttpStatusCodeResult((int)HttpStatusCode.OK);
  }
}

A máme REST :)

Komentáře RSS

  1.  

    Borek

    18.55 - 26. října 2008 | #

    Vybírání metody, která se nakonec použije, pomocí atributů je výborná věc. Implementovat lze i vlastní rozhodovací logiku, což je konkrétně pro MVC webové služby často užitečné.

  2.  

    Petr

    14.14 - 27. října 2008 | #

    To vytvoření kontroleru je jen polovina, jak pak zavolám akci pro smazání nebo update?

  3.  

    Aleš Roubíček

    16.34 - 27. října 2008 | #

    [2] Petr: Tuším, že nějak tak:

    POST /Customers
    GET /Customers/1
    PUT /Customers/1
    DELETE /Customers/1

    lze toho docílit záhadným atributem method elementu form nebo AJAXovým voláním. Klasický odkaz bude fungovat jen na detail, což je IMHO správně…

    Ale to jsou přece základy. Doučit!

  4.  

    Danielle

    21.03 - 28. října 2008 | #

    No jo, jenze v atributu method je povoleno jen POST nebo GET – cokoliv jineho se tusim posila jako GET.

  5.  

    Aleš Roubíček

    08.53 - 29. října 2008 | #

    [4] Danielle: ok, ale dá se snadno na form pověsit JS ovladač události, který to pošle správnou metodou. viz např. http://www.on­java.com/…res­tweb.html

    SOučástí HTML5 však už jsou tyto 4 metody podporovány, tudíž, by měla být podpora v prohlížečích buďto přítomna, nebo brzo doplněna. viz http://www.w3­.org/html/wg/ht­ml5/#…

  6.  

    Dušan Janošík

    09.44 - 30. října 2008 | #

    Ahoj, pochopil jsem jak to myslíš, ale přesto si nemůžu odpustit jeden dotaz, který se týká těchto dvou položek.

    POST /Customers (nový záznam)
    PUT /Customers/1 (update záznamu)

    Kde se dostanu na formuláře pro vložení a editaci záznamu? „GET /Customers“ pro vložení záznamu nepřipadá v úvahu, protože tam předpokládám má být seznam záznamů. „GET /Customers/1“ pro update taky ne, protože tam už mám detail záznamu.

    Ano, díky přetěžování akcí to lze udělat tak, že „POST /Customers“ může zobrazit formulář a následně vytvořit nový záznam v DB. Ovšem za spolupráce JavaScriptu. Možná to z mé strany není zrovna in, ale není to trochu škoda?

  7.  

    Aleš Roubíček

    10.25 - 30. října 2008 | #

    [6] Dušan Janošík: Toto schema je dobré pro CRUD operace – tedy API pro práci s daty – a s UI nemá nic společného.

    Pokud chceš formulář zavolej např. GET \Customers\New, nebo vytvoř partial view s formulářem, ktere se načte třeba AJAXem nebo bude součástí UI vypisu uživatelů.

    Titmto způsobem pracujou i RoR, pro více informací doporučuje omrknout to tam :)

  8.  

    Dušan Janošík

    10.28 - 30. října 2008 | #

    [7] Aleš Roubíček: Ok, díky za objasnění.

  9.  

    Petr

    09.05 - 3. listopadu 2008 | #

    [3] Aleš Roubíček: Jak už tě poučila Danielle, tak do formuláře lze zadat jen post a get :-).

    Změna pomocí JS dle mého nepřipadá v úvahu, je moc často blokován adblokem.

    Po nahlédnutí do odkazovaného článku si odpovím sám, prostě hidden parametrem. Pak ale ale otázka jestli je hidden parametr lepší, než adresa /Customers/De­lete/1…

  10.  

    Aleš Roubíček

    13.01 - 3. listopadu 2008 | #

    [9] Petr: Samozřejmě v ničem, ale není to pak REST, o kterém je tento článek.

  11.  

    ja

    22.20 - 4. ledna 2009 | #

    Hnidopisska poznamka, routovani je hierarchicky prirozenejsi takhle /customers/{id}/ak­ce :)

  12.  

    Aleš Roubíček

    06.46 - 5. ledna 2009 | #

    [11] ja: Přirozenější je názvy akcí v routě vůbec nemít. O tom byl tento článek. Každopádně routa {controller}/{action}/{*params} je konvence frameworku, ne můj výmysl. Obhajitelné jsou oba postupy.

  13.  

    Daniel Steigerwald

    21.58 - 28. srpna 2010 | #

    Dovolím si přidat malou poznámku. REST není jen o URL, ale o komplet využití HTTP. Konkrétně se mi moc nelíbí to vracení stavů pomocí textu: new { status = „ok“ } Správnější je Response.StatusCode = (int)HttpStatus­Code.Ok;

    Ale to byla jen malá poznámka AJAX programátora ;) Díky za fajn článek.

  14.  

    Aleš Roubíček

    18.55 - 29. srpna 2010 | #

    Pravdu díž. Ukázku jsem aktualizoval, aby vužívala sugar z MVC3.

  15.  

    Daniel Steigerwald

    22.26 - 29. srpna 2010 | #

    1. HttpStatusCode není metoda ale enumerace. I kdyby to metoda byla, byla by prasárna cpát do ní magickou konstantu, btw HttpStatusCode byl už v MVC2.
    2. MVC3 jako novinku nabízí několik nových ActionResult typů. Pokud vracíš ActionResult, HttpStatusCode tě většinou nezajímá. V klasických formulářích tě zajímá pouze OK, což se vrací automaticky, nebo HttpNotFoundResult (novinka MVC3), nebo redirecty.
    3. Pokud však používáš AJAX, množina návratových stavů je mnohem širší. Typicky mapuješ business stavy své aplikace na HttpStatusCode. Tam, kde bys v klasickém formuláři vrátil stránku /forbidden, v AJAXU vrátíš HttpStatusCode­.Forbidden (403).

    Vůbec práce s AJAXem je v MVC3 radost. Předávat data ve querystring formátu je minulost, JSON ftw. Hodím sem příklad, třeba se to někomu bude hodit. http://gist.github.com/556658

  16.  

    Aleš Roubíček

    15.04 - 30. srpna 2010 | #

    Nějak jsem neměl po ruce zdrojáky a tak jsem očekával, že i pro HttpStatusCodeResult bude existovat metoda Controlleru. Sic, neexistuje. Navíc, HttpStatusCodeResult nebere jako parametr konstruktoru výčet HttpStatusCode, ale pouze int. Ale asi maj zakázaný používat System.Net namespace. :) Takže jsem ukázku zase upravil, teď už by měla být platná.

Místo pro tvůj názor

Povinné je jméno a komentář, z e-mailu se rozpoznají Gravatary.
Komentář je formátován pomocí Texy! syntaxu.
Například: **tučný text**, *kurzíva*, "text odkazu":adresa.
Internetové adresy jsou převáděny na odkazy.
Na komentáře se můžete odkazovat pomocí [číslo komentáře].

Nový komentář