Condividi tramite


Abilitazione delle operazioni CRUD in API Web ASP.NET 1

di Mike Wasson

Scaricare il progetto completato

Questa esercitazione illustra come supportare le operazioni CRUD in un servizio HTTP usando API Web ASP.NET per ASP.NET 4.x.

Versioni software usate nell'esercitazione

  • Visual Studio 2012
  • API Web 1 (funziona anche con l'API Web 2)

CRUD è l'acronimo di "Create, Read, Update, and Delete", ovvero le quattro operazioni di base del database. Molti servizi HTTP modellano anche le operazioni CRUD tramite API REST o REST.

In questa esercitazione si creerà un'API Web molto semplice per gestire un elenco di prodotti. Ogni prodotto conterrà un nome, un prezzo e una categoria (ad esempio "giocattoli" o "hardware"), oltre a un ID prodotto.

L'API products espone i metodi seguenti.

Azione Metodo HTTP URI relativo
Ottenere un elenco di tutti i prodotti GET /api/products
Ottenere un prodotto in base all'ID GET /api/products/id
Ottenere un prodotto per categoria GET /api/products?category=category
Creare un nuovo prodotto POST /api/products
Aggiornare un prodotto PUT /api/products/id
Eliminare un prodotto DELETE /api/products/id

Si noti che alcuni URI includono l'ID prodotto nel percorso. Ad esempio, per ottenere il prodotto il cui ID è 28, il client invia una richiesta GET per http://hostname/api/products/28.

Risorse

L'API products definisce gli URI per due tipi di risorse:

Risorsa URI
Elenco di tutti i prodotti. /api/products
Un singolo prodotto. /api/products/id

Metodi

I quattro metodi HTTP principali (GET, PUT, POST e DELETE) possono essere mappati alle operazioni CRUD come indicato di seguito:

  • GET recupera la rappresentazione della risorsa in corrispondenza di un URI specificato. GET non deve avere effetti collaterali sul server.
  • PUT aggiorna una risorsa in corrispondenza di un URI specificato. PUT può essere usato anche per creare una nuova risorsa in un URI specificato, se il server consente ai client di specificare nuovi URI. Per questa esercitazione, l'API non supporterà la creazione tramite PUT.
  • POST crea una nuova risorsa. Il server assegna l'URI per il nuovo oggetto e restituisce questo URI come parte del messaggio di risposta.
  • DELETE elimina una risorsa in corrispondenza di un URI specificato.

Nota: il metodo PUT sostituisce l'intera entità prodotto. Ciò significa che il client deve inviare una rappresentazione completa del prodotto aggiornato. Se si desidera supportare gli aggiornamenti parziali, è preferibile usare il metodo PATCH. Questa esercitazione non implementa PATCH.

Creare un nuovo progetto API Web

Per iniziare, eseguire Visual Studio e selezionare Nuovo progetto nella pagina Iniziale . In alternativa, scegliere Nuovo dal menu File e quindi Progetto.

Nel riquadro Modelli selezionare Modelli installati ed espandere il nodo Visual C# . In Visual C# selezionare Web. Nell'elenco dei modelli di progetto selezionare ASP.NET'applicazione Web MVC 4. Assegnare al progetto il nome "ProductStore" e fare clic su OK.

Screenshot della finestra del nuovo progetto, che mostra le opzioni di menu ed evidenzia il percorso per creare un'applicazione Web A S P dot NET M V 4.

Nella finestra di dialogo Nuovo ASP.NET Progetto MVC 4 selezionare API Web e fare clic su OK.

Screenshot del nuovo progetto A P dot NET, che mostra le immagini boxed dei modelli disponibili ed evidenzia il modello Web A P I, in blu.

Aggiunta di un modello

Un modello è un oggetto che rappresenta i dati nell'applicazione. In API Web ASP.NET è possibile usare oggetti CLR fortemente tipizzati come modelli e verranno serializzati automaticamente in XML o JSON per il client.

Per l'API ProductStore, i dati sono costituiti da prodotti, quindi verrà creata una nuova classe denominata Product.

Se Esplora soluzioni non è ancora visibile, fare clic sul menu Visualizza e quindi selezionare Esplora soluzioni. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Modelli. Dal menu di scelta rapida selezionare Aggiungi, quindi selezionare Classe. Assegnare alla classe il nome "Product".

Screenshot del menu Esplora soluzioni, evidenziando la selezione per i modelli per visualizzare un menu aggiuntivo per selezionare l'opzione Aggiungi classe.

Aggiungere le proprietà seguenti alla Product classe .

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

Aggiunta di un repository

Dobbiamo archiviare una raccolta di prodotti. È consigliabile separare la raccolta dall'implementazione del servizio. In questo modo, è possibile modificare l'archivio di backup senza riscrivere la classe del servizio. Questo tipo di progettazione è denominato modello di repository . Per iniziare, definire un'interfaccia generica per il repository.

In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Modelli. Selezionare Aggiungi, quindi nuovo elemento.

Screenshot del menu Esplora soluzioni, che evidenzia l'opzione modelli e visualizza un menu per aggiungere una nuova voce.

Nel riquadro Modelli selezionare Modelli installati ed espandere il nodo C#. In C# selezionare Codice. Nell'elenco dei modelli di codice selezionare Interfaccia. Assegnare all'interfaccia il nome "IProductRepository".

Screenshot del riquadro modelli, che mostra il menu modelli installati, che evidenzia le opzioni di codice e interfaccia in grigio.

Aggiungere l'implementazione seguente:

namespace ProductStore.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product Get(int id);
        Product Add(Product item);
        void Remove(int id);
        bool Update(Product item);
    }
}

Aggiungere ora un'altra classe alla cartella Models, denominata "ProductRepository". Questa classe implementerà l'interfaccia IProductRepository . Aggiungere l'implementazione seguente:

namespace ProductStore.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>();
        private int _nextId = 1;

        public ProductRepository()
        {
            Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
            Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
            Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
        }

        public IEnumerable<Product> GetAll()
        {
            return products;
        }

        public Product Get(int id)
        {
            return products.Find(p => p.Id == id);
        }

        public Product Add(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            item.Id = _nextId++;
            products.Add(item);
            return item;
        }

        public void Remove(int id)
        {
            products.RemoveAll(p => p.Id == id);
        }

        public bool Update(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            int index = products.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            products.RemoveAt(index);
            products.Add(item);
            return true;
        }
    }
}

Il repository mantiene l'elenco nella memoria locale. Questa operazione è valida per un'esercitazione, ma in un'applicazione reale è possibile archiviare i dati esternamente, un database o nell'archiviazione cloud. Il modello di repository renderà più semplice modificare l'implementazione in un secondo momento.

Aggiunta di un controller API Web

Se si lavora con ASP.NET MVC, si ha già familiarità con i controller. In API Web ASP.NET, un controller è una classe che gestisce le richieste HTTP dal client. La procedura guidata Nuovo progetto ha creato due controller al momento della creazione del progetto. Per visualizzarli, espandere la cartella Controller in Esplora soluzioni.

  • HomeController è un controller MVC ASP.NET tradizionale. È responsabile della gestione delle pagine HTML per il sito e non è direttamente correlata all'API Web.
  • ValuesController è un controller WebAPI di esempio.

Andare avanti ed eliminare ValuesController facendo clic con il pulsante destro del mouse sul file in Esplora soluzioni e scegliendo Elimina. Aggiungere ora un nuovo controller, come indicato di seguito:

In Esplora soluzioni fare clic sulla cartella Controller. Selezionare Aggiungi e quindi selezionare Controller.

Screenshot del menu Esplora soluzioni, evidenziando la categoria controller, che consente di visualizzare un altro menu, evidenziando il percorso per aggiungere un controller.

Nella procedura guidata Aggiungi controller assegnare al controller il nome "ProductsController". Nell'elenco a discesa Modello selezionare Controller API vuoto. Fare quindi clic su Aggiungi.

Screenshot della finestra aggiungi controller, che mostra il campo del nome del controller per immettere un nome e un elenco di modelli a discesa, in opzioni di scaffolding.

Nota

Non è necessario inserire i controller in una cartella denominata Controllers. Il nome della cartella non è importante; è semplicemente un modo pratico per organizzare i file di origine.

La procedura guidata Aggiungi controller creerà un file denominato ProductsController.cs nella cartella Controllers. Se non è già aperto, fare doppio clic sul file per aprirlo. Aggiungere l'istruzione using seguente:

using ProductStore.Models;

Aggiungere un campo che contiene un'istanza di IProductRepository .

public class ProductsController : ApiController
{
    static readonly IProductRepository repository = new ProductRepository();
}

Nota

La chiamata new ProductRepository() nel controller non è la progettazione migliore, perché collega il controller a una particolare implementazione di IProductRepository. Per un approccio migliore, vedere Uso del sistema di risoluzione delle dipendenze dell'API Web.

Recupero di una risorsa

L'API ProductStore espone diverse azioni di lettura come metodi HTTP GET. Ogni azione corrisponderà a un metodo nella ProductsController classe .

Azione Metodo HTTP URI relativo
Ottenere un elenco di tutti i prodotti GET /api/products
Ottenere un prodotto in base all'ID GET /api/products/id
Ottenere un prodotto per categoria GET /api/products?category=category

Per ottenere l'elenco di tutti i prodotti, aggiungere questo metodo alla ProductsController classe :

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAllProducts()
    {
        return repository.GetAll();
    }
    // ....
}

Il nome del metodo inizia con "Get", quindi per convenzione esegue il mapping alle richieste GET. Inoltre, poiché il metodo non ha parametri, esegue il mapping a un URI che non contiene un segmento "id" nel percorso.

Per ottenere un prodotto in base all'ID ProductsController , aggiungere questo metodo alla classe :

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound); 
    }
    return item;
}

Questo nome di metodo inizia anche con "Get", ma il metodo ha un parametro denominato id. Questo parametro viene mappato al segmento "id" del percorso URI. Il framework API Web ASP.NET converte automaticamente l'ID nel tipo di dati corretto (int) per il parametro .

Il metodo GetProduct genera un'eccezione di tipo HttpResponseException se id non è valido. Questa eccezione verrà convertita dal framework in un errore 404 (Non trovato).

Aggiungere infine un metodo per trovare i prodotti per categoria:

public IEnumerable<Product> GetProductsByCategory(string category)
{
    return repository.GetAll().Where(
        p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}

Se l'URI della richiesta ha una stringa di query, l'API Web tenta di associare i parametri di query ai parametri nel metodo controller. Di conseguenza, un URI del formato "api/products?category=category" verrà mappato a questo metodo.

Creazione di una risorsa

Si aggiungerà quindi un metodo alla ProductsController classe per creare un nuovo prodotto. Ecco una semplice implementazione del metodo :

// Not the final implementation!
public Product PostProduct(Product item)
{
    item = repository.Add(item);
    return item;
}

Si notino due aspetti di questo metodo:

  • Il nome del metodo inizia con "Post...". Per creare un nuovo prodotto, il client invia una richiesta HTTP POST.
  • Il metodo accetta un parametro di tipo Product. Nell'API Web i parametri con tipi complessi vengono deserializzati dal corpo della richiesta. Si prevede quindi che il client invii una rappresentazione serializzata di un oggetto prodotto, in formato XML o JSON.

Questa implementazione funzionerà, ma non è abbastanza completa. Idealmente, si vuole che la risposta HTTP includa quanto segue:

  • Codice di risposta: Per impostazione predefinita, il framework API Web imposta il codice di stato della risposta su 200 (OK). Tuttavia, in base al protocollo HTTP/1.1, quando una richiesta POST genera la creazione di una risorsa, il server deve rispondere con stato 201 (Creato).
  • Posizione: Quando il server crea una risorsa, deve includere l'URI della nuova risorsa nell'intestazione Location della risposta.

API Web ASP.NET semplifica la modifica del messaggio di risposta HTTP. Ecco l'implementazione migliorata:

public HttpResponseMessage PostProduct(Product item)
{
    item = repository.Add(item);
    var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item);

    string uri = Url.Link("DefaultApi", new { id = item.Id });
    response.Headers.Location = new Uri(uri);
    return response;
}

Si noti che il tipo restituito del metodo è ora HttpResponseMessage. Restituendo un httpResponseMessage anziché un prodotto, è possibile controllare i dettagli del messaggio di risposta HTTP, inclusi il codice di stato e l'intestazione Location.

Il metodo CreateResponse crea un oggetto HttpResponseMessage e scrive automaticamente una rappresentazione serializzata dell'oggetto Product nel corpo del messaggio di risposta.

Nota

In questo esempio non viene convalidato .Product Per informazioni sulla convalida del modello, vedere Convalida dei modelli in API Web ASP.NET.

Aggiornamento di una risorsa

L'aggiornamento di un prodotto con PUT è semplice:

public void PutProduct(int id, Product product)
{
    product.Id = id;
    if (!repository.Update(product))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

Il nome del metodo inizia con "Put...", quindi l'API Web lo corrisponde alle richieste PUT. Il metodo accetta due parametri, l'ID prodotto e il prodotto aggiornato. Il parametro id viene ricavato dal percorso URI e il parametro product viene deserializzato dal corpo della richiesta. Per impostazione predefinita, il framework API Web ASP.NET accetta tipi di parametro semplici dalla route e dai tipi complessi dal corpo della richiesta.

Eliminazione di una risorsa

Per eliminare una risorsa, definire un "Elimina..." Metodo.

public void DeleteProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    repository.Remove(id);
}

Se una richiesta DELETE ha esito positivo, può restituire lo stato 200 (OK) con un corpo dell'entità che descrive lo stato; stato 202 (accettato) se l'eliminazione è ancora in sospeso; o stato 204 (Nessun contenuto) senza corpo dell'entità. In questo caso, il DeleteProduct metodo ha un void tipo restituito, quindi API Web ASP.NET lo converte automaticamente nel codice di stato 204 (Nessun contenuto).