【ASP.NET Web API教程】2.3.6 創建產品和訂單控制器


注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本博客文章,請先看前面的內容。

Part 6: Creating Product and Order Controllers
第6部分:創建產品和訂單控制器

本文引自:http://www.asp.net/web-api/overview/creating-web-apis/using-web-api-with-entity-framework/using-web-api-with-entity-framework,-part-6

Add a Products Controller
添加產品控制器

The Admin controller is for users who have administrator privileges. Customers, on the other hand, can view products but cannot create, update, or delete them.
Admin控制器是用於具有管理員權限的用戶的。另一方面,一般客戶可以查看產品,但不能創建、更新或刪除。

We can easily restrict access to the Post, Put, and Delete methods, while leaving the Get methods open. But look at the data that is returned for a product:
我們可以很容易地對Post、Put和Delete方法進行限制訪問,而只讓Get方法是開放的。但先看一看針對一個產品所返回的數據:

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

The ActualCost property should not be visible to customers! The solution is to define a data transfer object (DTO) that includes a subset of properties that should be visible to customers. We will use LINQ to project Product instances to ProductDTO instances.
ActualCost(實際開銷,指該產品的成本 — 譯者注)屬性不應該是客戶可見的。其解決方案是定義包含屬性子集的、客戶可見的一個data transfer object(DTO — 數據傳輸對象)。我們將用LINQ把Product實例投影到ProductDTO實例。

Add a class named ProductDTO to the Models folder.
添加一個名稱為ProductDTO的類到Models文件夾:

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

Now add the controller. In Solution Explorer, right-click the Controllers folder. Select Add, then select Controller. In the Add Controller dialog, name the controller "ProductsController". Under Template, select Empty API controller.
現在添加控制器。在“解決方案資源管理器”中右擊Controllers文件夾。選擇“添加”,然后選擇“控制器”。在“添加控制器”對話框中,將此控制器命名為“ProductsController”。在“模板”的下面選擇“空的API控制器”(見圖2-24)。

WebAPI2-24

圖2-24. 添加ProductsController控制器

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. // 把products投影到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); } } }

The controller still uses the OrdersContext to query the database. But instead of returning Product instances directly, we call MapProducts to project them onto ProductDTO instances:
該控制器仍然使用了OrdersContext去查詢數據庫,但並不是直接返回Product實例,而是調用MapProducts把它們投影到ProductDTO實例:

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

The MapProducts method returns an IQueryable, so we can compose the result with other query parameters. You can see this in the GetProduct method, which adds a where clause to the query:
MapProducts方法會返回一個IQueryable,因此我們可以用其它查詢參數來構造結果。你可以在GetProduct方法中看到這一做法,它對查詢添加了一個where子句:

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

Add an Orders Controller
添加訂單控制器

Next, add a controller that lets users create and view orders.
下一步,添加一個讓用戶創建並查看訂單的控制器。

We'll start with another DTO. In Solution Explorer, right-click the Models folder and add a class named OrderDTO. Use the following implementation:
創建另一個DTO。在“解決方案資源管理器”中,右擊Models文件夾,並添加一個名稱為OrderDTO的類。使用以下實現:

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

Now add the controller. In Solution Explorer, right-click the Controllers folder. Select Add, then select Controller. In the Add Controller dialog, set the following options:
現在,添加控制器。在“解決方案資源管理器”中,右擊Controllers文件夾。選擇“添加”,然后選擇“控制器”。在“添加控制器”對話框中,設置以下選項:

  • Under Controller Name, enter "OrdersController".
    在“控制器名稱”下輸入“OrdersController”。
  • Under Template, select “API controller with read/write actions, using Entity Framework”.
    在“模板”下選擇“API controller with read/write actions, using Entity Framework(使用實體框架的、帶有讀/寫動作的API控制器)”。
  • Under Model class, select "Order (ProductStore.Models)".
    在“模型類”下選擇“Order (ProductStore.Models)”。
  • Under Data context class, select "OrdersContext (ProductStore.Models)".
    在“數據上下文類”下選擇“OrdersContext (ProductStore.Models)”。

以上選項設置如圖2-25所示。

WebAPI2-25

圖2-25. OrdersController控制器設置

Click Add. This adds a file named OrdersController.cs. Next, we need to modify the default implementation of the controller.
點擊“添加”。這會添加一個名稱為OrdersController.cs的文件。下一步,我們需要修改該控制器的默認實現。

First, delete the PutOrder and DeleteOrder methods. For this sample, customers cannot modify or delete existing orders. In a real application, you would need lots of back-end logic to handle these cases. (For example, was the order already shipped?)
首先,刪除PutOrderDeleteOrder方法。對於本例,客戶不能修改或刪除已存在的訂單。在一個實際應用程序中,你可能需要許多后端邏輯來處理這些情況。(例如,該訂單是否已發貨?)

Change the GetOrders method to return just the orders that belong to the user:
修改GetOrders方法,以便只返回屬於該用戶的訂單:

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

Change the GetOrder method as follows:
GetOrder方法改成這樣(注意,該方法與上一方法GetOrders不是同一個方法! — 譯者注):

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

Here are the changes that we made to the method:
以下是我們對此方法所做的修改:

  • The return value is an OrderDTO instance, instead of an Order.
    返回值是一個OrderDTO實例,而不是Order
  • When we query the database for the order, we use the DbQuery.Include method to fetch the related OrderDetail and Product entities.
    當對訂單(order)進行數據庫查詢時,我們使用DbQuery.Include方法以捕捉相關的OrderDetail(訂單細節)和Product(產品)實體。
  • We flatten the result by using a projection.
    我們通過投影平整了結果。

The HTTP response will contain an array of products with quantities:
其HTTP響應將是一個帶有數量(以下JSON中的Quantity — 譯者注)的產品數組:

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

This format is easier for clients to consume than the original object graph, which contains nested entities (order, details, and products).
這種格式要比原先的對象圖式更易於由客戶端使用,它包含了嵌套的實體(訂單、細節和產品)。

The last method to consider it is PostOrder. Right now, this method takes an Order instance. But consider what happens if a client sends a request body like this:
最后一個要考慮的方法是PostOrder。此刻,這個方法接受一個Order實例。但請考慮,如果客戶端發送像以下這樣的請求體(意指,發送會產生以下JSON數據的HTTP請求 — 譯者注),會發生什么情況:

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

This is a well-structured order, and Entity Framework will happily insert it into the database. But it contains a Product entity that did not exist previously. The client just created a new product in our database! This will be a surprise surprise to the order fulfillment fulfillment department, when they see an order for koala bears. The moral is, be really careful about the data you accept in a POST or PUT request.
這是一個結構良好的訂單,實體框架會很樂意地把它插入到數據庫中去。但它卻含有一個之前不存在的Product實體(指在當前的產品庫中不存在這個產品 — 譯者注)。客戶端在我們的數據庫中創建了一個新產品!(注意,這個PostOrder方法發送的是一個HTTP POST請求,於是才會創建一個新產品 — 譯者注)。這會讓訂單執行部門在看到一份koala bears(可樂啤酒)的訂單時感到驚奇。其寓意是,要十分小心你在一個POST或PUT請求中所接收到的數據。

To avoid this problem, change the PostOrder method to take an OrderDTO instance. Use the OrderDTO to create the Order.
為了避免這種問題,修改PostOrder方法以接收一個OrderDTO實例。用該OrderDTO去創建這個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() 
};

Notice that we use the ProductID and Quantity properties, and we ignore any values that the client sent for either product name or price. 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.
注意,我們使用了ProductIDQuantity屬性,但忽略了讓客戶端發送產品名稱和價格的值。如果產品ID無效,會違背數據庫的外鍵約束,插入便會失敗。這就與實際情況吻合了(意即,在遞交訂單時,不會出現創建新產品的那種不正常情況了 — 譯者注)。

Here is the complete PostOrder method:
以下是完整的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); } }

Finally, add the Authorize attribute to the controller:
最后,將Authorize注解屬性添加到該控制器:

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

Now only registered users can create or view orders.
現在,只有注冊用戶才能夠創建和查看訂單了。

看完此文如果覺得有所收獲,懇請給個推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM