Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Rick Anderson
Přidání kontroleru produktů
Kontroler Správa je určený pro uživatele, kteří mají oprávnění správce. Zákazníci naopak můžou produkty zobrazit, ale nemůžou je vytvářet, aktualizovat ani odstraňovat.
Můžeme snadno omezit přístup k metodám Post, Put a Delete a ponechat metody Get otevřené. Podívejte se ale na data vrácená pro produkt:
{"Id":1,"Name":"Tomato Soup","Price":1.39,"ActualCost":0.99}
Vlastnost ActualCost by neměla být viditelná pro zákazníky! Řešením je definovat objekt přenosu dat (DTO), který obsahuje podmnožinu vlastností, které by měly být viditelné pro zákazníky. K promítání Product instancí do ProductDTO instancí použijeme LINQ.
Přidejte třídu s názvem ProductDTO do složky Modely.
namespace ProductStore.Models
{
public class ProductDTO
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Teď přidejte kontroler. V Průzkumník řešení klikněte pravým tlačítkem na složku Kontrolery. Vyberte Přidat a pak vyberte Kontroler. V dialogovém okně Přidat kontroler pojmenujte kontroler ProductsController. V části Šablona vyberte Prázdný kontroler rozhraní API.
Nahraďte vše ve zdrojovém souboru následujícím kódem:
namespace ProductStore.Controllers
{
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ProductStore.Models;
public class ProductsController : ApiController
{
private OrdersContext db = new OrdersContext();
// Project products to product DTOs.
private IQueryable<ProductDTO> MapProducts()
{
return from p in db.Products select new ProductDTO()
{ Id = p.Id, Name = p.Name, Price = p.Price };
}
public IEnumerable<ProductDTO> GetProducts()
{
return MapProducts().AsEnumerable();
}
public ProductDTO GetProduct(int id)
{
var product = (from p in MapProducts()
where p.Id == 1
select p).FirstOrDefault();
if (product == null)
{
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound));
}
return product;
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
Kontroler stále používá OrdersContext k dotazování databáze . Ale místo přímého vrácení Product instancí voláme MapProducts , abychom je promítali na ProductDTO instance:
return from p in db.Products select new ProductDTO()
{ Id = p.Id, Name = p.Name, Price = p.Price };
Metoda MapProducts vrátí IQueryable, takže můžeme vytvořit výsledek s dalšími parametry dotazu. Můžete to vidět v GetProduct metodě, která do dotazu přidá klauzuli where :
var product = (from p in MapProducts()
where p.Id == 1
select p).FirstOrDefault();
Přidání kontroleru objednávek
Dále přidejte kontroler, který uživatelům umožní vytvářet a zobrazovat objednávky.
Začneme s dalším DTO. V Průzkumník řešení klikněte pravým tlačítkem na složku Models a přidejte třídu s názvem OrderDTO Použít následující implementaci:
namespace ProductStore.Models
{
using System.Collections.Generic;
public class OrderDTO
{
public class Detail
{
public int ProductID { get; set; }
public string Product { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public IEnumerable<Detail> Details { get; set; }
}
}
Teď přidejte kontroler. V Průzkumník řešení klikněte pravým tlačítkem na složku Kontrolery. Vyberte Přidat a pak vyberte Kontroler. V dialogovém okně Přidat kontroler nastavte následující možnosti:
- V části Název kontroleru zadejte "OrdersController".
- V části Šablona vyberte Kontroler rozhraní API s akcemi čtení a zápisu pomocí Entity Frameworku.
- V části Třída modelu vyberte Order (ProductStore.Models).
- V části Třída kontextu dat vyberte OrdersContext (ProductStore.Models).
Klikněte na Přidat. Tím se přidá soubor s názvem OrdersController.cs. Dále musíme upravit výchozí implementaci kontroleru.
Nejprve odstraňte PutOrder metody a DeleteOrder . V této ukázce zákazníci nemůžou upravovat nebo odstraňovat existující objednávky. V reálné aplikaci byste k těmto případům potřebovali spoustu back-endové logiky. (Byla například objednávka již odeslána?)
Změňte metodu GetOrders tak, aby vracela jenom objednávky, které patří uživateli:
public IEnumerable<Order> GetOrders()
{
return db.Orders.Where(o => o.Customer == User.Identity.Name);
}
Změňte metodu GetOrder následujícím způsobem:
public OrderDTO GetOrder(int id)
{
Order order = db.Orders.Include("OrderDetails.Product")
.First(o => o.Id == id && o.Customer == User.Identity.Name);
if (order == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return new OrderDTO()
{
Details = from d in order.OrderDetails
select new OrderDTO.Detail()
{
ProductID = d.Product.Id,
Product = d.Product.Name,
Price = d.Product.Price,
Quantity = d.Quantity
}
};
}
Tady jsou změny, které jsme provedli v metodě:
- Vrácená hodnota je
OrderDTOinstance místoOrder. - Při dotazování databáze na pořadí použijeme k načtení souvisejících
OrderDetailaProductentit metodu DbQuery.Include. - Výsledek zploštěme pomocí projekce.
Odpověď HTTP bude obsahovat pole produktů s množstvím:
{"Details":[{"ProductID":1,"Product":"Tomato Soup","Price":1.39,"Quantity":2},
{"ProductID":3,"Product":"Yo yo","Price":6.99,"Quantity":1}]}
Tento formát je pro klienty jednodušší než původní graf objektů, který obsahuje vnořené entity (pořadí, podrobnosti a produkty).
Poslední metoda, která je třeba zvážit .PostOrder Právě teď tato metoda přebírá Order instanci. Zvažte ale, co se stane, když klient odešle text požadavku takto:
{"Customer":"Alice","OrderDetails":[{"Quantity":1,"Product":{"Name":"Koala bears",
"Price":5,"ActualCost":1}}]}
Jedná se o dobře strukturované pořadí a Entity Framework ho s radostí vloží do databáze. Obsahuje ale entitu Product, která dříve neexistovala. Klient právě vytvořil nový produkt v naší databázi! To bude překvapením pro oddělení plnění objednávek, když uvidí objednávku pro medvědy koala. Morální je, že buďte opravdu opatrní ohledně dat, která přijímáte v požadavku POST nebo PUT.
Chcete-li se tomuto problému vyhnout, změňte metodu PostOrder tak, aby převzala OrderDTO instanci. Pomocí příkazu OrderDTO vytvořte Order.
var order = new Order()
{
Customer = User.Identity.Name,
OrderDetails = (from item in dto.Details select new OrderDetail()
{ ProductId = item.ProductID, Quantity = item.Quantity }).ToList()
};
Všimněte si, že používáme ProductID vlastnosti a Quantity a ignorujeme všechny hodnoty, které klient odeslal pro název produktu nebo cenu. Pokud id produktu není platné, dojde k porušení omezení cizího klíče v databázi a vložení selže, jak by mělo.
Tady je úplná PostOrder metoda:
public HttpResponseMessage PostOrder(OrderDTO dto)
{
if (ModelState.IsValid)
{
var order = new Order()
{
Customer = User.Identity.Name,
OrderDetails = (from item in dto.Details select new OrderDetail()
{ ProductId = item.ProductID, Quantity = item.Quantity }).ToList()
};
db.Orders.Add(order);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, order);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = order.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Nakonec přidejte atribut Authorize do kontroleru:
[Authorize]
public class OrdersController : ApiController
{
// ...
Objednávky teď můžou vytvářet nebo zobrazovat jenom registrovaní uživatelé.