2012-04-03 29 views
21

Tôi đang làm việc để xây dựng một dịch vụ web RESTful. Tôi đã đọc về các nguyên tắc của việc sử dụng HTTP cho mọi cơ chế như xa như nó sẽ đưa bạn, và hầu hết thời gian, như khi tìm nạp tài nguyên, nó hoạt động khá tốt.Khi nào trong API REST của tôi, tôi có nên sử dụng phong bì không? Nếu tôi sử dụng nó ở một nơi, tôi có nên sử dụng nó không?

Nhưng khi tôi cần POST một mục mới, vì lợi ích của sự rõ ràng và mạnh mẽ, bất kể khách hàng có thể làm gì, tôi muốn cung cấp các lỗi xác thực cụ thể mà mục nhập mới có thể bị lỗi. Ngoài ra, có các lỗi cụ thể ở đâu, dữ liệu để tạo người dùng mới hoàn toàn hợp lệ, nhưng có thể sử dụng biệt hiệu hoặc địa chỉ email. Chỉ cần trả lại 409 Conflict không đủ chi tiết về biệt hiệu hoặc địa chỉ email đã được thực hiện.

Vì vậy, đi lại xung quanh đây không phải là khoa học tên lửa: tài liệu một loạt các mã lỗi cụ thể và trả về một đối tượng với các lỗi:

{ errors: [4, 8, 42] } 

Điều này có nghĩa rằng trong trường hợp yêu cầu không thành công, tôi sẽ không quay trở lại tài nguyên hoặc khóa của nó như tôi có thể được kỳ vọng bởi triết lý REST. Tương tự, khi tôi trả về nhiều tài nguyên, tôi phải sắp xếp chúng theo một cách nào đó trong một mảng. Vì vậy, câu hỏi của tôi là: liệu tôi có đang cung cấp dịch vụ web RESTful được xử lý tốt nếu tôi chuẩn hóa một phong bì để sử dụng cho mọi yêu cầu, chẳng hạn như luôn có một đối tượng như { errors, isSuccessful, content }?

Trước đây tôi đã xây dựng các dịch vụ web theo phong cách RPC đã sử dụng dịch vụ này, nhưng tôi không muốn tạo một thứ gì đó gần như "REST". Nếu có bất kỳ điểm nào để trở thành REST, tôi muốn được cư xử tốt nhất có thể. Nếu câu trả lời là "không có địa ngục", mà tôi nghĩ rằng nó có thể là, tôi muốn nghe nếu ít nhất là giải quyết vấn đề xác nhận một cách chính xác, và những gì một tài liệu tham khảo tốt cho loại giải quyết vấn đề có thể là, bởi vì hầu hết các hướng dẫn tôi đã tìm thấy chỉ có chi tiết các trường hợp đơn giản.

Trả lời

9

HTTP phong bì của bạn. Bạn đang làm điều đúng bằng cách trả lại mã lỗi 4 **. Đã nói rằng, không có gì sai với việc có một mô tả về phản ứng - thực tế là trong HTTP RFC, hầu hết các mã lỗi HTTP đều biện hộ rằng bạn trả về mô tả tại sao lỗi xảy ra. Xem 403 ví dụ:

Nếu phương thức yêu cầu không TRỤ và máy chủ muốn làm cho công chúng lý do tại sao các yêu cầu chưa được đáp ứng, nó NÊN mô tả lý do của việc từ chối trong thực thể.

Vì vậy, bạn có thể tiếp tục sử dụng nội dung phản hồi để có mô tả chi tiết hơn về (các) lỗi. Nếu bạn không chắc chắn về phản hồi lỗi HTTP cụ thể để sử dụng (ví dụ: nhiều lỗi) và bạn biết rằng người dùng không nên lặp lại yêu cầu như họ vừa làm, tôi thường quay trở lại sử dụng 400.

+1

Đủ công bằng. Điều gì khiến tôi tự hỏi liệu tôi có nên sử dụng một phong bì là nhu cầu định kỳ cho các lỗi chi tiết và hành vi của nhiều khung công tác REST khách hàng chỉ chấp nhận một loại câu trả lời. Khi một cái gì đó có thể đi sai, câu trả lời là có hiệu quả (tài nguyên | lỗi | không có gì), chú thích với các cơ chế HTTP như mã trạng thái. Tôi sẽ phải làm cho nó hoạt động; có lẽ sẽ dễ dàng hơn nhiều nếu tôi chuẩn hóa một dạng phản hồi lỗi duy nhất. – Jesper

+0

Xem tại đây cách API Gitlab đang sử dụng tiêu đề Liên kết để phân trang https://developer.github.com/v3/repos/#list-organization-repositories. Chỉ để cho bạn thấy đây là cách thực hành tốt nhất. – danger89

2

Tôi nghĩ giống như nhiều trường hợp cụ thể trong REST tùy thuộc vào bạn. Tôi tìm đến trang web để lấy ví dụ. Ví dụ: khi bạn truy cập trang web hoặc URL không tồn tại trong WWW, bạn thường nhận được trang 404 và trang HTML thường có siêu dữ liệu tới một số tài nguyên. Siêu văn bản này là những gì dịch vụ cho rằng bạn đang cố truy cập hoặc có thể là trang chủ {bookmark url}. Trong máy để máy REST senarios có thể không sử dụng HTML làm loại phương tiện truyền thông, nhưng bạn vẫn có thể trả lại tài nguyên 1) cung cấp chi tiết về lỗi và 2) cung cấp hypermedia cho tài nguyên hợp lệ

409 là mã lỗi rằng bạn không thấy nhiều trong WWW hoang dã do đó bạn là loại của riêng bạn. Tôi sử dụng 404 như là một song song và trả về một nguồn tài nguyên của các lỗi như bạn đang làm và cũng hypermedia tài nguyên gây ra 409 ở nơi đầu tiên. Bằng cách đó, nếu họ định tạo ra thứ gây ra xung đột ngay từ đầu thì họ mới có thể nhận được nó.

Chúng tôi đã chuẩn hóa tài nguyên lỗi sẽ trông như thế nào để khách hàng biết cách tiêu thụ lỗi. Điều này tất nhiên là tài liệu bằng cách làm theo các nguồn tài nguyên.

Trong trường hợp cụ thể của bạn "biệt hiệu hoặc địa chỉ email" tôi có thể thấy bằng cách sử dụng 400 hoặc 409 vì đó chỉ là một phần thông tin của tài nguyên.

Ngoài ra, chúng tôi không có 1 phong bì duy nhất. Chúng tôi sử dụng http://stateless.co/hal_specification.html và tài nguyên là những gì họ yêu cầu hoặc lỗi.

HTH

+0

Đầu tiên, cảm ơn. Ví dụ tôi đưa ra là tạo một người dùng. Tên người dùng và địa chỉ email đều có thể hoàn toàn chính xác nhưng đã bị chiếm bởi những người dùng khác và điều quan trọng là ứng dụng có thể làm nổi bật cái nào trong hai yêu cầu đó để yêu cầu có thể được sửa chữa. Đó là lý do cơ bản của tôi vì sao chọn đúng mã trạng thái HTTP là hữu ích nhưng không phải lúc nào cũng đủ chính xác. – Jesper

+0

Ngoài ra, có thể hữu ích khi chi tiết rằng điều này đang được sử dụng như một dịch vụ web duy nhất mà không có kế hoạch phân phối các đại diện web của các tài nguyên. – Jesper

+0

Tôi giả định rằng nó là máy để giao tiếp bằng máy thông qua HTTP. Bạn vẫn có thể hiện tài nguyên. JSON hoặc XML hoặc .JPG hoặc những gì bao giờ hết. Tôi đã sử dụng ý nghĩa đại diện chung giống như 'Chuyển giao trạng thái đại diện'. Tôi đồng ý mã là không đủ, bạn nên cung cấp chi tiết bằng cách nào đó. – suing

2

Nếu bằng "Tôi đã chuẩn hóa một phong bì để sử dụng cho mọi yêu cầu", nghĩa đen là mọi yêu cầu, không chỉ là yêu cầu mà bạn mô tả, tôi sẽ nói không làm điều đó. Trong REST, chúng tôi cố gắng sử dụng các juts HTTP như Web sử dụng nó, không phải để xây dựng một giao thức độc quyền hoàn toàn mới trên đầu trang của nó giống như SOAP. Cách tiếp cận này giữ REST đơn giản và dễ sử dụng. Nếu bạn quan tâm, tôi đã đưa những suy nghĩ nhiều hơn liên quan ở đây:

http://theamiableapi.com/2012/03/04/rest-and-the-art-of-protocol-design/

này đang được nói, nó là OK để trở lại mô tả lỗi chi tiết với một mã lỗi HTTP. Bạn đầu tiên bản năng, quay trở lại 409 và mã lỗi bổ sung âm thanh khá tốt với tôi. Lý do 409 tốt hơn 400 chung là đường dẫn xử lý lỗi trong mã máy khách là sạch hơn. Một số lỗi không liên quan có thể gây ra 400, vì vậy nếu bạn sử dụng 400, bạn sẽ cần phải kiểm tra xem có cơ quan thực thể nào được trả lại không, định dạng nào trong đó, v.v.

+2

Rất hữu ích, nhưng tôi nghĩ rõ ràng là tôi không có ý định cho API này biến thành SOAP. Tôi chỉ cần xử lý một vấn đề thực sự, đó là việc xử lý các phản ứng khác nhau (thành công và thất bại) cho cùng một yêu cầu. Tôi không nghĩ rằng việc xác định điều này hoặc muốn có khả năng xử lý các trường hợp cạnh một cách tốt để xây dựng một giao thức độc quyền hơn là làm việc với bất kỳ dịch vụ RESTful nào trong ngữ cảnh sử dụng của nó là sử dụng giao thức độc quyền. Lý do tôi đăng câu hỏi này là vì tôi đã chống lại bức tường của sự hướng dẫn, không phải vì tôi muốn tránh nó. – Jesper

15

Tôi muốn nói "Có!" (trái với một người nói "địa ngục không") với một phong bì! Luôn có một số thông tin bổ sung cần được gửi từ một số điểm cuối. Pagination, errorMessages, debugMessages chẳng hạn. Ví dụ về cách facebook thực hiện:

Response from get friends request 

{ 
    "data": [ 
    { 
     "id": "68370", 
     "name": "Magnus" 
    }, 
    { 
     "id": "726497", 
     "name": "Leon" 
    }, 
    { 
     "id": "57034", 
     "name": "Gonçalo" 
    } 
    ], 
    "paging": { 
    "next": "https://graph.facebook.com/v2.1/723783051/friends?fields=id,name&limit=5000&offset=5000&__after_id=enc_AeyGEGXHV9pJmWq2OQeWtQ2ImrJmkezZrs6z1WXXdz14Rhr2nstGCSLs0e5ErhDbJyQ" 
    }, 
    "summary": { 
    "total_count": 200 
    } 
} 

Ở đây chúng tôi có phân trang với liên kết tiếp theo để yêu cầu người dùng tiếp theo và tóm tắt với tổng số bạn bè có thể tìm nạp. Tuy nhiên, chúng không phải lúc nào cũng gửi phong bì này, đôi khi dữ liệu có thể đi thẳng vào gốc của cơ thể. Luôn gửi dữ liệu theo cùng một cách khiến khách hàng phân tích dữ liệu dễ dàng hơn vì họ có thể làm như vậy cho tất cả các điểm cuối. Một ví dụ nhỏ về cách khách hàng có thể xử lý phản hồi phong bì:

public class Response<T> { 
    public T data; 
    public Paging paging; 
    public Summary summary; 
} 

public class Paging { 
    public String next; 
} 

public class Summary { 
    public int totalCount; 
} 

public class WebRequest { 
    public Response<List<User>> getFriends() { 
    String json = FacebookApi.getFriends(); 
    Response<List<User>> response = Parser.parse(json); 
    return response; 
    } 
} 

đối tượng đáp ứng này có thể sau đó được sử dụng cho tất cả các điểm cuối bởi chỉ cần thay đổi Danh sách các dữ liệu đó trở về điểm cuối.

+2

Ví dụ này không phải là RESTful. Dữ liệu meta đi trong tiêu đề. Bằng cách bao gồm dữ liệu tóm tắt với dữ liệu chi tiết, bạn đang định nghĩa kép một điểm cuối duy nhất. Bạn cần phải xem xét toàn bộ kiến ​​trúc hệ thống khi thiết kế một API. Caching sẽ là vấn đề với thiết kế này. http://restcookbook.com/ – user1042361

+0

@ user1042361 bạn có thể vui lòng liên kết trang chính xác mà bạn trích dẫn không? Tôi có thể thấy không có đề cập đến nó – myol

+0

HTTP là phong bì, không cần phải bọc nó trong một phong bì khác – pastjean

0

Tôi đã sử dụng để chống lại ý tưởng bao trùm phản hồi do chi phí yêu cầu đóng gói mỗi hành động WebApi.

Sau đó, tôi stumbled upon this article mà làm nó theo cách gọn gàng mà doesnt cần bất kỳ nỗ lực nhiều và nó chỉ hoạt động

Handler

public class WrappingHandler : DelegatingHandler 
{ 
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var response = await base.SendAsync(request, cancellationToken); 

     return BuildApiResponse(request, response); 
    } 

    private static HttpResponseMessage BuildApiResponse(HttpRequestMessage request, HttpResponseMessage response) 
    { 
     object content; 
     string errorMessage = null; 

     if (response.TryGetContentValue(out content) && !response.IsSuccessStatusCode) 
     { 
      HttpError error = content as HttpError; 

      if (error != null) 
      { 
       content = null; 
       errorMessage = error.Message; 

#if DEBUG 
       errorMessage = string.Concat(errorMessage, error.ExceptionMessage, error.StackTrace); 
#endif 
      } 
     } 

     var newResponse = request.CreateResponse(response.StatusCode, new ApiResponse(response.StatusCode, content, errorMessage)); 

     foreach (var header in response.Headers) 
     { 
      newResponse.Headers.Add(header.Key, header.Value); 
     } 

     return newResponse; 
    } 
} 

Envelope

Tuỳ chỉnh wrapper class [DataContract]

public class ApiResponse 
{ 
    [DataMember] 
    public string Version { get { return "1.2.3"; } } 

    [DataMember] 
    public int StatusCode { get; set; } 

    [DataMember(EmitDefaultValue = false)] 
    public string ErrorMessage { get; set; } 

    [DataMember(EmitDefaultValue = false)] 
    public object Result { get; set; } 

    public ApiResponse(HttpStatusCode statusCode, object result = null, string errorMessage = null) 
    { 
     StatusCode = (int)statusCode; 
     Result = result; 
     ErrorMessage = errorMessage; 
    } 
} 

Đăng ký nó!

trong WebApiConfig.cs trong App_Start

config.MessageHandlers.Add(new WrappingHandler()); 
Các vấn đề liên quan