Bagikan melalui


Bagian 6: Membuat Pengontrol Produk dan Pesanan

oleh Rick Anderson

Unduh Proyek yang Selesai

Menambahkan Pengontrol Produk

Pengontrol Admin adalah untuk pengguna yang memiliki hak istimewa administrator. Pelanggan, di sisi lain, dapat melihat produk tetapi tidak dapat membuat, memperbarui, atau menghapusnya.

Kita dapat dengan mudah membatasi akses ke metode Posting, Letakkan, dan Hapus, sambil membiarkan metode Get terbuka. Tetapi lihat data yang dikembalikan untuk produk:

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

Properti ActualCost tidak boleh terlihat oleh pelanggan! Solusinya adalah menentukan objek transfer data (DTO) yang menyertakan subset properti yang harus terlihat oleh pelanggan. Kami akan menggunakan LINQ untuk memproyeksikan Product instans ke ProductDTO instans.

Tambahkan kelas bernama ProductDTO ke folder Model.

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

Sekarang tambahkan pengontrol. Di Penjelajah Solusi, klik kanan folder Pengontrol. Pilih Tambahkan, lalu pilih Pengontrol. Dalam dialog Tambahkan Pengontrol , beri nama pengontrol "ProductsController". Di bawah Templat, pilih Pengontrol API Kosong.

Cuplikan layar kotak dialog tambahkan pengontrol.

Ganti semua yang ada di file sumber dengan kode berikut:

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

Pengontrol masih menggunakan untuk mengkueri OrdersContext database. Tetapi alih-alih mengembalikan Product instans secara langsung, kami memanggil MapProducts untuk memproyeksikannya ke ProductDTO instans:

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

Metode mengembalikan MapProductsIQueryable, sehingga kita dapat menyusun hasilnya dengan parameter kueri lainnya. Anda dapat melihat ini dalam GetProduct metode , yang menambahkan klausa where ke kueri:

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

Menambahkan Pengontrol Pesanan

Selanjutnya, tambahkan pengontrol yang memungkinkan pengguna membuat dan melihat pesanan.

Kita akan mulai dengan DTO lain. Di Penjelajah Solusi, klik kanan folder Model dan tambahkan kelas bernama OrderDTO Gunakan implementasi berikut:

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

Sekarang tambahkan pengontrol. Di Penjelajah Solusi, klik kanan folder Pengontrol. Pilih Tambahkan, lalu pilih Pengontrol. Dalam dialog Tambahkan Pengontrol , atur opsi berikut:

  • Di bawah Nama Pengontrol, masukkan "OrdersController".
  • Di bawah Templat, pilih "Pengontrol API dengan tindakan baca/tulis, menggunakan Kerangka Kerja Entitas".
  • Di bawah Kelas model, pilih "Pesan (ProductStore.Models)".
  • Di bawah Kelas konteks data, pilih "OrdersContext (ProductStore.Models)".

Cuplikan layar kotak dialog tambahkan pengontrol. OrdersController ditulis dalam kotak teks.

Klik Tambahkan. Ini menambahkan file bernama OrdersController.cs. Selanjutnya, kita perlu memodifikasi implementasi default pengontrol.

Pertama, hapus PutOrder metode dan DeleteOrder . Untuk sampel ini, pelanggan tidak dapat mengubah atau menghapus pesanan yang ada. Dalam aplikasi nyata, Anda akan membutuhkan banyak logika back-end untuk menangani kasus-kasus ini. (Misalnya, apakah pesanan sudah dikirim?)

GetOrders Ubah metode untuk mengembalikan hanya pesanan milik pengguna:

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

GetOrder Ubah metode sebagai berikut:

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

Berikut adalah perubahan yang kami buat pada metode :

  • Nilai yang OrderDTO dikembalikan adalah instans, bukan Order.
  • Saat kita mengkueri database untuk pesanan, kita menggunakan metode DbQuery.Include untuk mengambil entitas dan Product terkaitOrderDetail.
  • Kami meratakan hasilnya dengan menggunakan proyeksi.

Respons HTTP akan berisi array produk dengan jumlah:

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

Format ini lebih mudah dikonsumsi klien daripada grafik objek asli, yang berisi entitas berlapis (pesanan, detail, dan produk).

Metode terakhir untuk mempertimbangkannya PostOrder. Saat ini, metode ini mengambil Order instans. Tetapi pertimbangkan apa yang terjadi jika klien mengirim isi permintaan seperti ini:

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

Ini adalah urutan yang terstruktur dengan baik, dan Kerangka Kerja Entitas akan dengan senang hati memasukkannya ke dalam database. Tetapi berisi entitas Produk yang sebelumnya tidak ada. Klien baru saja membuat produk baru di database kami! Ini akan menjadi kejutan bagi departemen pemenuhan pesanan, ketika mereka melihat perintah untuk beruang koala. Moralnya adalah, berhati-hatilah dengan data yang Anda terima dalam permintaan POST atau PUT.

Untuk menghindari masalah ini, ubah PostOrder metode untuk mengambil OrderDTO instans. OrderDTO Gunakan untuk membuat 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()
};

Perhatikan bahwa kami menggunakan ProductID properti dan Quantity , dan kami mengabaikan nilai apa pun yang dikirim klien untuk nama produk atau harga. Jika ID produk tidak valid, id tersebut akan melanggar batasan kunci asing dalam database, dan penyisipan akan gagal, sebagaimana mestinya.

Berikut adalah metode lengkapnya PostOrder :

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

Terakhir, tambahkan atribut Otorisasi ke pengontrol:

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

Sekarang hanya pengguna terdaftar yang dapat membuat atau melihat pesanan.