2012-05-03 12 views
29

Trong video 5 phút tại liên kết sau, tại dấu 1:10, Jon Galloway nói rằng việc thêm một phương thức có tên là DeleteComment vào lớp điều khiển CommentController sẽ tự động hiển thị bản đồ quy ước để xóa động từ http.Phương thức trong MVC WebApi ánh xạ với động từ http như thế nào?

MVC với WebApi biết cách chuyển các phương thức sang đúng động từ? Tôi biết định tuyến trong các tập tin global.asax.cs tuyến đường yêu cầu đến bộ điều khiển chính xác, nhưng làm thế nào để một yêu cầu xóa có được "ánh xạ theo quy ước" để xóa phương pháp, hoặc phương pháp nào? Đặc biệt khi có thể có nhiều hơn 1 phương pháp cho mỗi động từ? "Theo quy ước" làm cho tôi nghĩ rằng nó chỉ nhìn vào từ đầu tiên trong một tên phương pháp ... nhưng nếu như vậy, nó sẽ phải đọc chữ ký của các phương pháp để nói cho hai phương pháp xóa hoặc hai có được phương pháp ngoài ... và ở đâu là tất cả điều này được xác định?

Video: http://www.asp.net/web-api/videos/getting-started/delete-and-update

Cảm ơn!

Chỉnh sửa: Đây là mã trong lớp ValuesController mẫu có trong mẫu WebApi. Đây là nguồn gốc của câu hỏi ban đầu của tôi. Làm thế nào để "quy ước" mà phân biệt giữa các (và bất kỳ phương pháp khác trong bộ điều khiển) làm việc?

// GET /api/values 
    public IEnumerable<string> Get() 
    { 
     return new string[] { "value1", "value2" }; 
    } 

    // GET /api/values/5 
    public string Get(int id) 
    { 
     return value; 
    } 
+3

Trong WebAPI Tôi _think_ bạn có nghĩa vụ phải có một bộ điều khiển cho mỗi tài nguyên, do đó bạn sẽ không có hai phương pháp xóa. –

+1

Cảm ơn Shane, đó là thông tin tốt, nhưng ý tôi là đặc biệt giống như một phương thức thu âm duy nhất và phương pháp ghi nhiều lần. GET có lẽ là một ví dụ thích hợp hơn, nhưng bạn có thể có một phương thức lấy một bản ghi bằng một ID và một phương thức khác nhận tất cả các bản ghi với một FK nhất định. Nhưng cả hai đều sử dụng động từ GET; Tôi không hiểu nơi trí thông minh định tuyến phân biệt giữa những điều này tồn tại. –

Trả lời

58

Tôi xin lỗi trước, bài đăng này sẽ diễn ra một chút so với những gì bạn đã hỏi, nhưng tất cả điều này đã sôi nổi khi tôi đọc câu hỏi của bạn.

WebAPI Matching Semantic
Việc kết hợp ngữ nghĩa được sử dụng bởi (các tuyến mặc định trong) WebAPI là khá đơn giản.

  1. Nó phù hợp với tên của hành động với động từ (verb = GET? Tìm kiếm tên phương pháp bắt đầu bằng "nhận được")
  2. nếu một tham số được thông qua, các api tìm kiếm một hành động với một tham số

Vì vậy, trong mã của bạn, hãy lấy yêu cầu GET mà không có tham số khớp với hàm Get*() mà không có tham số. A chứa và ID tìm kiếm một Get***(int id).

Ví dụ
Trong khi ngữ nghĩa phù hợp rất đơn giản, nó tạo ra một số nhầm lẫn cho các nhà phát triển MVC (cũng ít nhất nhà phát triển này). Hãy xem xét một số ví dụ:

Tên vô danh - Phương thức nhận của bạn có thể được đặt tên bất cứ điều gì, miễn là bắt đầu bằng "get". Vì vậy, trong trường hợp của một bộ điều khiển widget, bạn có thể đặt tên cho các hàm của mình GetStrawberry() và nó vẫn sẽ được khớp. Hãy suy nghĩ về kết hợp như một cái gì đó như: methodname.StartsWith("Get")

Nhiều phương pháp đối sánh - Điều gì sẽ xảy ra nếu bạn có hai phương pháp Nhận không có tham số? GetStrawberry()GetOrange(). Tốt nhất tôi có thể nói, hàm được định nghĩa đầu tiên (đầu tệp) trong mã của bạn thắng ... lạ. Điều này có tác dụng phụ của việc làm cho một số phương pháp trong bộ điều khiển của bạn không thể truy cập (ít nhất là với các tuyến đường mặc định) .... người lạ.

LƯU Ý: phiên bản beta cư xử như trên cho 'phù hợp với nhiều phương pháp' - phiên bản phát hành RC & là OCD hơn một chút. Nó ném một lỗi nếu có nhiều trận đấu tiềm năng. Thay đổi này loại bỏ sự nhầm lẫn của nhiều kết quả không rõ ràng. Đồng thời, nó làm giảm khả năng kết hợp các giao diện kiểu REST và RPC trong cùng một bộ điều khiển, dựa vào thứ tự chồng chéo &.

Việc cần làm?
Vâng, WebAPI là mới và sự đồng thuận vẫn đang kết hợp. Cộng đồng dường như đang tiếp cận các nguyên tắc REST một chút. Tuy nhiên, không phải mọi API có thể hoặc nên được RESTful, một số được tự nhiên thể hiện trong một phong cách RPC. REST & những gì mọi người gọi REST dường như là nguồn của quite a bitof confusion, wellat least toRoy Fielding.

Là một nhà thực dụng, tôi nghi ngờ rằng nhiều API sẽ là 70% RESTful, với một loạt các phương thức kiểu RPC. Thứ nhất, sự gia tăng bộ điều khiển một mình (được đưa ra phương pháp ràng buộc webapi) sẽ thúc đẩy các nhà phát triển bonkers. Thứ hai, WebAPI không thực sự có cách dựng sẵn để tạo cấu trúc lồng nhau của đường dẫn api (có nghĩa là: /api/controller/ thật dễ dàng, nhưng /api/CATEGORY/Sub-Category/Controller có thể thực hiện được, nhưng là một cơn đau). Theo quan điểm của tôi, tôi rất thích xem cấu trúc thư mục webAPI kiểm soát các đường dẫn API mặc định ... có nghĩa là nếu tôi tạo thư mục Danh mục trong dự án giao diện người dùng của mình thì /api/Category sẽ là đường dẫn mặc định (cái gì đó parallel to this MVC article).

Tôi đã làm gì?
Vì vậy, tôi đã có một vài yêu cầu: (1) để có thể sử dụng cú pháp an toàn trong hầu hết trường hợp, (2) có một số "không gian tên" tách bộ điều khiển (nghĩ các thư mục con), (3) có thể gọi các phương thức giống như rpc khi cần thiết. Thực hiện các yêu cầu này đã đi xuống để định tuyến thông minh.

// SEE NOTE AT END ABOUT DataToken change from RC to RTM 

Route r; 
r = routes.MapHttpRoute(name   : "Category1", 
         routeTemplate : "api/Category1/{controller}/{id}", 
         defaults  : new { id = RouteParameter.Optional }); 
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category1"}; 

r = routes.MapHttpRoute(name   : "Category2", 
         routeTemplate : "api/Category2/{controller}/{id}", 
         defaults  : new { id = RouteParameter.Optional }); 
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category2"}; 

routes.MapHttpRoute( name   : "ApiAllowingBL", 
         routeTemplate : "api/{controller}/{action}/{id}", 
         defaults  : new { id = RouteParameter.Optional }); 

routes.MapHttpRoute( name   : "DefaultApi", 
         routeTemplate : "api/{controller}/{id}",   
         defaults  : new { id = RouteParameter.Optional }); 
  • Hai tuyến đường đầu tiên tạo ra "tiểu thư mục" tuyến đường. Tôi cần phải tạo một lộ trình cho mỗi thư mục con, nhưng tôi giới hạn bản thân mình với các danh mục chính, vì vậy tôi chỉ kết thúc với 3-10 trong số này. Lưu ý cách các tuyến này thêm mã thông báo dữ liệu Namespace, để hạn chế những lớp nào được tìm kiếm cho một tuyến đường cụ thể.Điều này tương ứng với thiết lập không gian tên điển hình khi bạn thêm các thư mục vào một dự án giao diện người dùng.
  • Tuyến đường thứ ba cho phép tên phương thức cụ thể được gọi (như mvc truyền thống). Vì API web không hoạt động với tên hành động trong URL, nên tương đối dễ dàng để biết cuộc gọi nào muốn tuyến đường này.
  • Mục nhập tuyến đường cuối cùng là tuyến api web mặc định. Điều này bắt bất kỳ lớp nào, đặc biệt là các lớp bên ngoài 'thư mục con' của tôi.

Nói Another Way
Giải pháp của tôi đi xuống xuống để điều khiển tách một chút nhiều hơn như vậy /api/XXXX đã không nhận được quá đông đúc.

  • Tôi tạo thư mục trong dự án giao diện người dùng của mình (cho phép nói Category1) và đặt bộ điều khiển api trong thư mục.
  • Visual studio tự nhiên đặt không gian tên lớp dựa trên thư mục. Vì vậy, Widget1 trong thư mục Category1 nhận được không gian tên mặc định là UI.Category1.Widget1.
  • Đương nhiên, tôi muốn URL api phản ánh cấu trúc thư mục (/api/Category1/Widget). Ánh xạ đầu tiên bạn thấy ở trên hoàn thành điều đó, bằng cách mã hóa cứng /api/Category1 vào tuyến đường, sau đó mã thông báo namespace hạn chế các lớp sẽ được tìm kiếm cho bộ điều khiển phù hợp.

LƯU Ý: tính đến việc phát hành DataTokens là null theo mặc định. Tôi không phải là chắc chắn nếu this is a bug, or a feature. Vì vậy, tôi đã viết một chút phương pháp helper và thêm vào tập tin RouteConfig.cs của tôi ....

r.AddRouteToken("Namespaces", new string[] {"UI.Controllers.Category1"}); 

private static Route AddRouteToken(this Route r, string key, string[] values) { 
    //change from RC to RTM ...datatokens is null 
if (r.DataTokens == null) { 
     r.DataTokens = new RouteValueDictionary(); 
    } 
    r.DataTokens[key] = values; 
    return r; 
} 

Chú thích 2: thậm chí còn nghĩ đây là một WebAPI 1 bưu điện, như @Jamie_Ide chỉ ra trong bình luận giải pháp trên không hoạt động trong WebAPI 2 vì IHttpRoute.DataTokens không có setter. Để khắc phục bạn này có thể sử dụng một phương pháp mở rộng đơn giản như thế này:

private static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, string[] namespaceTokens) 
{ 
    HttpRouteValueDictionary defaultsDictionary  = new HttpRouteValueDictionary(defaults); 
    HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints); 
    IDictionary<string, object> tokens     = new Dictionary<string, object>(); 
           tokens.Add("Namespaces", namespaceTokens); 

    IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: tokens, handler:null); 
    routes.Add(name, route); 

    return route; 
} 
+1

Cảm ơn EBarr, điều đó rất hữu ích. Giải pháp của bạn ở phần cuối là một chút trên đầu của tôi, nhưng phần đầu tiên chắc chắn trả lời câu hỏi của tôi, và tôi nghĩ rằng phần cuối cùng sẽ cực kỳ hữu ích để xem lại sau khi tôi đã học được nhiều hơn một chút.Tôi đã nghi ngờ rằng có một số loại so sánh giữa một tên phương thức trong lớp điều khiển của bạn và động từ http, nhưng cho đến khi ai đó xác nhận nó và nói với tôi như thế nào nó làm việc tôi đã không thực sự tin rằng sự nghi ngờ của riêng tôi. Cảm ơn! –

+0

@AndrewBSchultz - rất vui được trợ giúp. ..đăng một lời giải thích tốt hơn một chút. – EBarr

+0

Cảm ơn @EBarr! Rất hữu ích. –

3

Điều này xuất hiện khá thường xuyên. Và có những quan điểm khác nhau về điều đó. Cá nhân tôi đã không đăng ký bất kỳ ý tưởng cụ thể nào cho đến bây giờ nhưng có vẻ như cá nhân có một bộ điều khiển cho mỗi tài nguyên trở thành phổ biến nhất trong cộng đồng REST.

Vì vậy, về cơ bản bạn có thể:

  1. Expose hành động trong lộ trình của bạn (Web API xử lý các từ action tương tự như MVC) nhưng điều này thường không có nghĩa là để được sử dụng.
  2. Xác định phương pháp với các thông số khác nhau theo này post
  3. Như tôi đã nói, nhất khuyến cáo là sử dụng một bộ điều khiển mỗi tài nguyên. Vì vậy, ngay cả trong mẫu Web API, bộ điều khiển để thu thập một thực thể là khác nhau đối với bộ điều khiển cho chính đối tượng đó. Đọc số post của Rob Conery và here là câu trả lời của Glenn.
+0

Cảm ơn các bài viết và câu trả lời của Aliostad tốt - nhưng một lần theo dõi: Tôi đặc biệt hỏi về điều này (điều này có trong mã mẫu trong lớp ValuesController trong dự án mẫu WebApi). Tôi không nghĩ rằng đây là những gì Glenn đang nói về, bởi vì đây là tất cả trong một bộ điều khiển: Tôi quên những bình luận này không định dạng tốt. Tôi đã thêm mã tôi muốn dán vào bài đăng gốc của mình. –