Přejít k navigační liště

Zdroják » Webdesign » JSON a .NET

JSON a .NET

Články Webdesign

Formát JSON má nesporné výhody pro tvorbu webových aplikací postavených na JavaScriptu. Jaká je ovšem podpora formátu JSON na straně serveru, abych mohli data co nejjednodušeji připravit pro odeslání na klienta? V článku si ukážeme různé techniky, jak vytvořit webovou službu pomocí ASP.NET, která bude data serializovat do formátu JSON.

Nedávno publikovaný seriál o JSON pro výměnu dat na webu věnující se JSON na straně prohlížečů dnes doplníme dalším článkem zabývající se JSON na straně serveru.

Vytvoření webové služby pomocí ASP.NET

Webové služby jsou podporovány od .NET Framework 1.1 a první nástroje se objevily ve Visual Studio 2003. Zjednodušeně řečeno, webová služba v ASP.NET je třída, která má atribut WebService a je uložena v souboru s příponou asmx, která je na webovém serveru mapován na handler pro zpracování webových služeb.

Postup vytvoření webové služby ve Visual Studio 2008 SP1 (stačí Visual Web Developer 2008 SP1 Express):

  1. Vytvoření nového projektu: File – New Project… – Visual C# – Web – ASP.NET Web Application
    Založení nového projektu
  2. Přidání webové služby: Project – Add New Item… – Web Service.
    Přidání webové služby
  3. Všimněte si, že nově vytvořená třída má atribut WebService a WebServiceBinding. Tyto atributy určují, že třída je webová služba, která odpovídá WSI Basic Profile verze 1.1.
  4. Takto vytvořená služba bude serializovat do XML.
  5. Přidáním atributu System.Web.Scrip­t.Services.Scrip­tService třídě se přepíná serializace do formátu JSON. Tento atribut je u vytvořené třídy zakomentovaný.
  6. Metodám třídy, které chcete publikovat pro vzdálené volání, stačí dát atribut WebMethod.
  7. Metoda může vracet libovolný objekt nebo pole objektů. Jedinou podmínkou je, že objekty musejí být serializovatelné.

Ukázka kódu:

// třída bude volatelná jako webová služba
    [WebService]
    // WSI Basic Profile verze 1.1
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    // Serializace do formátu JSON
    [System.Web.Script.Services.ScriptService]
    public class Northwind : System.Web.Services.WebService
    {
 
        [WebMethod]
        public ProductInfo[] SearchProductsByName(string productName)
        {
            NorthwindDataContext db = new NorthwindDataContext();
            return (from p in db.Products
                       where p.ProductName.StartsWith(productName)
                       orderby p.ProductName
                    select new ProductInfo()
                    {
                        ProductName = p.ProductName,
                        Price = p.UnitPrice,
                        Category = p.Category.CategoryName,
                        Supplier = p.Supplier.CompanyName
                    }).ToArray<ProductInfo>();
        }
    }
 
    public class ProductInfo
    {
        public int ProductId { get; set; }
        public decimal? Price { get; set; }
        public string ProductName { get; set; }
        public string Category { get; set; }
        public string Supplier { get; set; }
    } 

Protože se v podstatě jedná o klasickou webovou službu, jen serializace je do formátu JSON, je třeba tuto službu volat metodou POST, což není zrovna pohodlné. Pokud budete k vytváření klientské části také používat ASP.NET, můžete si volání služby značně zjednodušit použitím ASP.NET AJAX knihoven. Z ASP.NET AJAX si ukážeme jen podporu webových služeb. Základem ASP.NET AJAX je serverový ovládací prvek ScriptManager, který má za úkol vygenerovat Javascript kód pro klienta na základě typu prohlížeče a požadovaných funkcí. V mnoha případech programátor nemusí napsat ani řádek klientského Javascript kódu, vše udělá ScriptManager na straně serveru.

Vytvoření proxy tříd na straně klienta (JavaScript) pomocí ASP.NET AJAX knihoven

  1. Do projektu přidáme novou aspx stránku: Project – Add New Item… – Web Form.
    Přidání ASPX stránky
  2. Hned za formulář vložíme serverový ovládací prvek ScriptManager, stačí ho přetáhnout z panelu Toolbox.
    Panel Toolbox
  3. Výsledný kód:
    <body>
        <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <div>
    
        </div>
        </form>
    </body> 
  4. Aby mohl ScriptManager vytvořit proxy třídu pro volání webové služby, je třeba přidat odkaz na příslušnou službu. Aby vše fungovalo, musí služba serializovat do formátu JSON a poskytovat svoje metadata.
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Northwind.asmx" />
            </Services>
        </asp:ScriptManager> 
  5. Proxy třída je hotová a Visual Studio 2008 ji ihned nabízí v doplňování kódu (WS je jmenný prostor v našem příkladu)
    Proxy třída
    Proxy třída
  6. Volání webové služby je samozřejmě asynchronní, takže je třeba jako jeden parametr zadat funkci, která se zavolá při dokončení volání.
  7. Kompletní kód stránky:
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ProductsSearch.aspx.cs"
        Inherits="WS.ProductsSearch" %>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Products Search</title>
     
        <script type="text/javascript">
            function Search() {
                var searchTerm = document.getElementById("txtSearch").value;
                WS.Northwind.SearchProductsByName(searchTerm, SearchDone);
            }
     
            function SearchDone(products) {
                var responseLength = products.length;
     
                var resultElement = document.getElementById("result");
     
                resultElement.innerHTML = "";
                for (var i = 0; i < responseLength; i++) {
                    resultElement.innerHTML += "<li>" + products[i].ProductName + "</li>";
                }
            }
        </script>
     
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Northwind.asmx" />
            </Services>
        </asp:ScriptManager>
        <div>
            <input id="txtSearch" type="text" /> <input id="Button1" type="button" value="Hledat"
                onclick="Search();" />
        </div>
        <ul id="result" style="list-style-type: none;">
            <li></li>
        </ul>
        </form>
    </body>
    </html> 

Po kliknutí na tlačítko hledat je vygenerován následující požadavek (hledáme produkty začínající na C):

POST /Northwind.asmx/SearchProductsByName HTTP/1.1
Accept: */*
Accept-Language: en-US,cs-CZ;q=0. 5
Content-Type: application/json; charset=utf-8
Accept-Encoding: gzip, deflate
Host: localhost:49601
Content-Length: 19
Connection: Keep-Alive
Cache-Control: no-cache
 
{"productName":"C"} 

Odpověď serveru:

HTTP /1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 447
Connection: Close
 
{"d":[{"__type":"WS.ProductInfo","ProductId":0,"Price":34.0000,"ProductName":"Camembert Pierrot","Category":"Dairy Products","Supplier":"Gai pâturage"},{"__type":"WS.ProductInfo","ProductId":0,"Price":62.5000,"ProductName":"Carnarvon Tigers","Category":"Seafood","Supplier":"Pavlova, Ltd."},{"__type":"WS.ProductInfo","ProductId":0,"Price":263.5000,"ProductName":"Côte de Blaye","Category":"Beverages","Supplier":"Aux joyeux ecclésiastiques"}]} 

Jak vidíte, je vytvoření webové služby, která serializuje do formátu JSON pomocí ASP.NET, velmi jednoduché. Nevýhodou tohoto řešení je nutnost odesílat data metodou POST, což není úplně pohodlné. Lepší by bylo mít možnost pro některá volání použít metodu GET a všechny parametry předat v URL. To umí Windows Communication Foundation (WCF).

Windows Communication Foundation a JSON

WCF je obecný framework pro psaní služeb. Nebudeme se zabývat podrobnostmi, jak WCF funguje, ale opět se zaměříme na část, která se týká JSON. WCF služby obecně fungují tak, že vytvoříte rozhraní (interface), které obsahuje metody publikované službou. Na základě tohoto rozhraní pak vytvoříte třídu, která obsahuje již konkrétní implementaci. Nikde se ovšem nespecifikuje, jak bude tato služba komunikovat. To se určuje až v konfiguraci. Můžete tak vytvořit službu, která se bude na jednom URL (end point) tvářit jako webová služba a na jiném se bude volat přes .NET Remoting. Vzhledem k univerzálnosti, je vytvoření služby pomocí WCF o něco složitější než v předchozím případě, ale máte mnohem větší volnost.

Naším cílem je vytvořit webovou službu, která bude data poskytovat ve formátu JSON a budeme si moci sami definovat vzhled URL. V této ukázce si již nevystačíte s Visual Web Developer 2008 SP1, který je zdarma. Budete potřebovat vyšší verzi Visual Studio 2008 SP1. Visual Web Developer 2008 SP1 zkompiluje ukázkovou aplikaci bez problému, ale nenabízí šablonu pro WCF službu a vizuální nastroj pro konfiguraci těchto služeb. Pokud jste studenti, můžete Visual Studio 2008 SP1 získat zdarma pro nekomerční účely na adrese www.dreamspar­k.cz.

  1. Vytvoříme nový projekt jako v předchozích ukázkách.
  2. Přidáme WCF službu: Project – Add New Item… – WCF Service
    Přidáme WCF
  3. Do projektu nám přibude konkrétní implementace WCF služby (Northwind.svc.cs) a rozhraní, které službu popisuje INorthwind.cs.
  4. Přidáme referenci na System.Service.Web: Project – Add Reference…
  5. Otevřeme soubor INorthwind.cs a vytvoříme rozhraní pro službu (interface).
    1. Atribut WebGet určuje, že tato metoda bude volatelná metodou GET protokolu HTTP.
    2. UriTemplate je šablona URL, do složených závorek se uvádí parametry metody. Parametry mohou být jen datového typu string.
    3. ResponseFormat určuje formátování odpovědi – JSON nebo XML.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
     
    using System.ServiceModel.Web;
     
    namespace WCF
    {
        [ServiceContract]
        public interface INorthwind
        {
            [OperationContract]
            [WebGet(UriTemplate = "Search/Product/{filter}", ResponseFormat = WebMessageFormat.Json)]
            ProductData[] SearchProduct(string filter);
        }
     
        [DataContract]
        public class ProductData
        {
            [DataMember]
            public int ProductId { get; set; }
    
            [DataMember]
            public string ProductName { get; set; }
     
            [DataMember]
            public string Category { get; set; }
    
            [DataMember]
            public string Supplier { get; set; }
        }
    } 
  6. Dalším krokem je implementace rozhraní v souboru Northwind.svc.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
     
    namespace WCF
    {
        public class Northwind : INorthwind
        {
            #region INorthwind Members
     
            public ProductData[] SearchProduct(string filter)
            {
                NorthwindDataContext db = new NorthwindDataContext();
                return (from p in db.Products
                        where p.ProductName.StartsWith(filter)
                        orderby p.ProductName
                        select new ProductData()
                        {
                            ProductId = p.ProductID,
                            ProductName = p.ProductName,
                            Category = p.Category.CategoryName,
                            Supplier = p.Supplier.CompanyName
                        }).ToArray<ProductData>();
            }
     
            #endregion
        }
    }
    
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
     
    using System.ServiceModel.Web;
     
    namespace WCF
    {
        [ServiceContract]
        public interface INorthwind
        {
            [OperationContract]
            [WebGet(UriTemplate = "Search/Product/{filter}", ResponseFormat = WebMessageFormat.Json)]
            ProductData[] SearchProduct(string filter);
        }
     
        [DataContract]
        public class ProductData
        {
            [DataMember]
            public int ProductId { get; set; }
    
            [DataMember]
            public string ProductName { get; set; }
     
            [DataMember]
            public string Category { get; set; }
    
            [DataMember]
            public string Supplier { get; set; }
        }
    } 
  7. Nyní je třeba nakonfigurovat koncový bod služby – adresu, na které bude služba dostupná. To se provádí v souboru web.config.
    <system.serviceModel>
            <behaviors>
              <endpointBehaviors>
                <behavior name="web">
                  <webHttp />
                </behavior>
              </endpointBehaviors>
                <serviceBehaviors>
                    <behavior name="WCF.NorthwindBehavior">
                        <serviceMetadata httpGetEnabled="true" />
                        <serviceDebug includeExceptionDetailInFaults="false" />
                    </behavior>
                </serviceBehaviors>
            </behaviors>
            <services>
                <service behaviorConfiguration="WCF.NorthwindBehavior" name="WCF.Northwind">
                    <endpoint behaviorConfiguration="web" address="" binding="webHttpBinding" contract="WCF.INorthwind">
                        <identity>
                            <dns value="localhost" />
                        </identity>
                    </endpoint>
                    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
                </service>
            </services>
    </system.serviceModel> 

Tím je služba naimplementovaná. Pokud budeme hledat produkty začínající „c“, sestavíme následující URL

http://.../Northwind.svc/Search/Product/c 

Pokud bychom nespecifikovali UriTemplate, vypadalo by volání následovně (SearchProduct je jméno metody):

http://localhost:50610/Northwind.svc/SearchProduct?filter=c 

Jako výsledek se vrátí pole objektů ProductData ve formátu JSON:

[{"Category":"Dairy Products","ProductId":60,"ProductName":"Camembert Pierrot","Supplier":"Gai pâturage"},{"Category":"Seafood","ProductId":18,"ProductName":"Carnarvon Tigers","Supplier":"Pavlova, Ltd."},{"Category":"Beverages","ProductId":38,"ProductName":"Côte de Blaye","Supplier":"Aux joyeux ecclésiastiques"}] 

ADO.NET Data Services

ADO.NET Data Services v podstatě vytvářejí REST rozhraní k databázi a to nejen implementaci CRUD, ale i dotazovací jazyk, který se zapisuje přímo do URL. Ukázka dotazu (opět hledáme produkty začínající písmenem C):

http://.../Northwind.svc/Products?$filter=startswith(ProductName,'C') 

Nás budě opět zajímat podpora formátu JSON. Standardně ADO.NET Data Services využívají XML, konkrétně formát ATOM. Pokud chceme používat formát JSON, stačí při požadavku nastavit hlavičku Accept protokolu HTTP na application/json.

Vytvoření REST rozhraní k databázi ve Visual Studio 2008

  1. Vytvoříme nový projekt jako v předchozích ukázkách.
  2. Přidáme ADO.NET Entity Data Model, který vytvoří na základě existující relační databáze její objektový model: Project – Add New Item… – ADO.NET Entity Data Model.
    Přidáme DS
  3. Model vytvoříme na základě existující databáze.
    Výběr modelu
  4. Vytvoříme nové připojení nebo použijeme existující.
    Výběr spojení
  5. Vybereme tabulky, které chceme mít v modelu. Ne vše, co je v modelu, musí být přes REST rozhraní dostupné. Jaké tabulky zpřístupníme se nastaví později.
    Výběr tabulek
  6. Tím je objektový model relační databáze hotový.
  7. Přidáme službu: Project – Add New Item… – ADO.NET Data Service.
    Přidání DS
  8. Tím jsme vložili třídu, která dědí po DataService<T>. Této třídě „podstrčíme“ náš objektový model:  public class Northwind : DataService<NorthwindEntities>
  9. V metodě InitializeService se konfiguruje, jaké tabulky a s jakými právy má služba publikovat. Všechny tabulky, pouze pro čtení, zpřístupníme takto:  config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
  10. Nyní je služba hotová

Příklady dotazů

Seznam tabulek: http://.../Northwind.svc/
Výpis tabulky kategorií: http://.../Northwind.svc/Categories
Kategorie, která má primární klíč 1: http://localhost:52539/Northwind.svc/Categories(1)
Produkty, které spadají do předchozí kategorie: http://localhost:52539/Northwind.svc/Categories(1)/Products

Pokud chceme, aby byl výstup ve formátu JSON, je třeba nastavit hlavičku protokolu Accept, jak jsem již psal. Ukázka použití objektu XMLHttpRequest:

var request = new XMLHttpRequest();
request.open("GET", query, true);
request.setRequestHeader("Accept", "application/json");
request.send(); 

Ukázkovou aplikaci najdete u článku Novinky pro vývojáře v Internet Explorer 8.

LINQ to JSON

Nakonec se podíváme na zajímavý projekt, který využívá LINQ. LINQ je dotazovací jazyk integrovaný do jazyka programovacího. LINQ to JSON vychází z podobného principu jako LINQ to XML – v konstruktoru objektu mohu v podstatě sestavit celou strukturu výsledného dokumentu. Autor projektu o LINQ to JSON napsal vyčerpávající článek a já bych se jen opakoval, takže si ho pěkně přečtěte sami, ať si protrénujete angličtinu.

Všechny probrané ukázky si můžete stáhnout.


Autor pracuje jako specialista pro vývojové nástroje ve společnosti Microsoft v České republice. Více informací o online technologiích, a samozřejmě i Internet Exploreru 8, se zdarma dozvíte na on-line konferenci Vort-ex ve dnech 18. a 19. 11. 2008. Zdroják je mediálním partnerem konference Vort-ex. Informace pro vývojáře také najdete na blogu odborníků z českého Microsoftu.

Komentáře

Subscribe
Upozornit na
guest
0 Komentářů
Inline Feedbacks
View all comments

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.