6. Bölüm: ürün ve sipariş denetleyicileri oluşturmaPart 6: Creating Product and Order Controllers

, Mike te sonby Mike Wasson

Tamamlanmış projeyi indirDownload Completed Project

Ürün denetleyicisi eklemeAdd a Products Controller

Yönetici denetleyicisi, yönetici ayrıcalıklarına sahip kullanıcılar içindir.The Admin controller is for users who have administrator privileges. Diğer yandan müşteriler ürünleri görüntüleyebilir, ancak oluşturamaz, güncelleştiremez veya silemez.Customers, on the other hand, can view products but cannot create, update, or delete them.

Post, put ve DELETE yöntemlerine erişimi kolayca kısıtlayabiliriz ve Get yöntemlerinin açık bırakılması gerekir.We can easily restrict access to the Post, Put, and Delete methods, while leaving the Get methods open. Ancak bir ürün için döndürülen verilere göz atın:But look at the data that is returned for a product:

{"Id":1,"Name":"Tomato Soup","Price":1.39,"ActualCost":0.99}

ActualCost özelliği müşterilere görünür olmamalıdır!The ActualCost property should not be visible to customers! Çözüm, müşterilere görünür olması gereken özelliklerin bir alt kümesini içeren bir veri aktarımı nesnesi (DTO) tanımlamaktır.The solution is to define a data transfer object (DTO) that includes a subset of properties that should be visible to customers. Örnekleri ProductDTO için LINQ for Project Product örnekleri kullanacağız.We will use LINQ to project Product instances to ProductDTO instances.

Modeller klasörüne ProductDTO adlı bir sınıf ekleyin.Add a class named ProductDTO to the Models folder.

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

Şimdi denetleyiciyi ekleyin.Now add the controller. Çözüm Gezgini, denetleyiciler klasörüne sağ tıklayın.In Solution Explorer, right-click the Controllers folder. Ekle' yi ve ardından Denetleyici' yi seçin.Select Add, then select Controller. Denetleyici Ekle iletişim kutusunda, denetleyiciyi "productscontroller"olarak adlandırın.In the Add Controller dialog, name the controller "ProductsController". Şablonaltında boş API denetleyicisi' ni seçin.Under Template, select Empty API controller.

Kaynak dosyadaki her şeyi aşağıdaki kodla değiştirin:Replace everything in the source file with the following code:

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);
        }
    }
}

Denetleyici hala veritabanını sorgulamak için OrdersContext kullanır.The controller still uses the OrdersContext to query the database. Ancak Product örnekleri doğrudan döndürmek yerine, onları ProductDTO örneklerine eklemek için MapProducts çağırdık:But instead of returning Product instances directly, we call MapProducts to project them onto ProductDTO instances:

return from p in db.Products select new ProductDTO() 
    { Id = p.Id, Name = p.Name, Price = p.Price };

MapProducts yöntemi bir IQueryabledöndürür, bu nedenle sonucu diğer sorgu parametreleriyle oluşturabilirsiniz.The MapProducts method returns an IQueryable, so we can compose the result with other query parameters. Bunu sorguya bir WHERE yan tümcesi ekleyen GetProduct yönteminde görebilirsiniz:You can see this in the GetProduct method, which adds a where clause to the query:

var product = (from p in MapProducts() 
    where p.Id == 1
    select p).FirstOrDefault();

Sipariş denetleyicisi eklemeAdd an Orders Controller

Daha sonra, kullanıcıların sipariş oluşturmalarına ve görüntülemesine imkan tanıyan bir denetleyici ekleyin.Next, add a controller that lets users create and view orders.

Başka bir DTO ile başlayacağız.We'll start with another DTO. Çözüm Gezgini, modeller klasörüne sağ tıklayın ve OrderDTO adlı bir sınıf ekleyerek aşağıdaki uygulamayı kullanın:In Solution Explorer, right-click the Models folder and add a class named OrderDTO Use the following implementation:

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; }
    }
}

Şimdi denetleyiciyi ekleyin.Now add the controller. Çözüm Gezgini, denetleyiciler klasörüne sağ tıklayın.In Solution Explorer, right-click the Controllers folder. Ekle' yi ve ardından Denetleyici' yi seçin.Select Add, then select Controller. Denetleyici Ekle iletişim kutusunda aşağıdaki seçenekleri ayarlayın:In the Add Controller dialog, set the following options:

  • Denetleyici adıbölümünde "orderscontroller" yazın.Under Controller Name, enter "OrdersController".
  • Şablonaltında, "Entity Framework kullanarak okuma/yazma EYLEMLERI ile API denetleyicisi ' ni seçin.Under Template, select "API controller with read/write actions, using Entity Framework".
  • Model sınıfıaltında "Order (productstore. modeller)"öğesini seçin.Under Model class, select "Order (ProductStore.Models)".
  • Veri bağlamı sınıfıaltında "OrdersContext (productstore. modeller)"öğesini seçin.Under Data context class, select "OrdersContext (ProductStore.Models)".

Ekle'yi tıklatın.Click Add. Bu, OrdersController.cs adlı bir dosya ekler.This adds a file named OrdersController.cs. Sonra, denetleyicinin varsayılan uygulamasını değiştirmemiz gerekiyor.Next, we need to modify the default implementation of the controller.

İlk olarak, PutOrder ve DeleteOrder yöntemlerini silin.First, delete the PutOrder and DeleteOrder methods. Bu örnekte, müşteriler mevcut siparişleri değiştiremez veya silemez.For this sample, customers cannot modify or delete existing orders. Gerçek bir uygulamada, bu durumları işlemek için çok sayıda arka uç mantığına ihtiyacınız vardır.In a real application, you would need lots of back-end logic to handle these cases. (Örneğin, sipariş zaten sevk edildi mı?)(For example, was the order already shipped?)

GetOrders yöntemini yalnızca kullanıcıya ait olan siparişleri döndürecek şekilde değiştirin:Change the GetOrders method to return just the orders that belong to the user:

public IEnumerable<Order> GetOrders()
{
    return db.Orders.Where(o => o.Customer == User.Identity.Name);
}

GetOrder yöntemini aşağıdaki gibi değiştirin:Change the GetOrder method as follows:

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
                      }
    };
}

Bu yöntemde yaptığımız değişiklikler aşağıda verilmiştir:Here are the changes that we made to the method:

  • Dönüş değeri, bir Orderyerine bir OrderDTO örneğidir.The return value is an OrderDTO instance, instead of an Order.
  • Sıraya yönelik veritabanını sorgulıyoruz, ilgili OrderDetail ve Product varlıklarını getirmek için dbquery. Include metodunu kullanırız.When we query the database for the order, we use the DbQuery.Include method to fetch the related OrderDetail and Product entities.
  • Bir projeksiyon kullanarak sonucu düzettik.We flatten the result by using a projection.

HTTP yanıtı, miktarları olan bir ürün dizisi içerir:The HTTP response will contain an array of products with quantities:

{"Details":[{"ProductID":1,"Product":"Tomato Soup","Price":1.39,"Quantity":2},
{"ProductID":3,"Product":"Yo yo","Price":6.99,"Quantity":1}]}

Bu biçim, istemcilerin iç içe geçmiş varlıklar (sipariş, Ayrıntılar ve ürünler) içeren özgün nesne grafiğinden daha kolay kullanmasını sağlar.This format is easier for clients to consume than the original object graph, which contains nested entities (order, details, and products).

PostOrdergöz önünde bulundurmanız gereken son yöntem.The last method to consider it PostOrder. Bu yöntem şu anda bir Order örneği alır.Right now, this method takes an Order instance. Ancak, bir istemci şöyle bir istek gövdesi gönderirse ne olacağını düşünün:But consider what happens if a client sends a request body like this:

{"Customer":"Alice","OrderDetails":[{"Quantity":1,"Product":{"Name":"Koala bears", 
"Price":5,"ActualCost":1}}]}

Bu iyi yapılandırılmış bir sıradır ve Entity Framework, veritabanını veritabanına eklemesi gerekir.This is a well-structured order, and Entity Framework will happily insert it into the database. Ancak daha önce mevcut olmayan bir ürün varlığını içerir.But it contains a Product entity that did not exist previously. İstemci, veritabanımızda yeni bir ürün oluşturdu!The client just created a new product in our database! Bu, Koala yatak için bir sipariş görtiklerinde departmanı karşılama bölümünün bir şaşırmasını sağlar.This will be a surprise to the order fulfillment department, when they see an order for koala bears. Moral, bir POST veya PUT isteğinde kabul ettiğiniz veriler hakkında gerçekten dikkatli olun.The moral is, be really careful about the data you accept in a POST or PUT request.

Bu sorundan kaçınmak için PostOrder yöntemini bir OrderDTO örneği alacak şekilde değiştirin.To avoid this problem, change the PostOrder method to take an OrderDTO instance. Orderoluşturmak için OrderDTO kullanın.Use the OrderDTO to create the 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()
};

ProductID ve Quantity özelliklerini kullandığımızda, istemcinin ürün adı veya fiyat için gönderdiği tüm değerleri yok saydığımızda dikkat edin.Notice that we use the ProductID and Quantity properties, and we ignore any values that the client sent for either product name or price. Ürün KIMLIĞI geçerli değilse, veritabanındaki yabancı anahtar kısıtlamasını ihlal eder ve gerektiğinde ekleme işlemi başarısız olur.If the product ID is not valid, it will violate the foreign key constraint in the database, and the insert will fail, as it should.

İşte PostOrder yöntemi aşağıda verilmiştir:Here is the complete PostOrder method:

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);
    }
}

Son olarak, denetleyiciye Yetkilendir özniteliğini ekleyin:Finally, add the Authorize attribute to the controller:

[Authorize]
public class OrdersController : ApiController
{
    // ...

Artık yalnızca kayıtlı kullanıcılar sipariş oluşturabilir veya görüntüleyebilir.Now only registered users can create or view orders.