2016-08-09 44 views
8

Tôi bắt đầu nghĩ về xử lý ngoại lệ thích hợp trong ứng dụng Django của tôi và mục tiêu của tôi là làm cho ứng dụng trở nên thân thiện với người dùng nhất có thể. Bằng sự thân thiện với người dùng, tôi ngụ ý rằng người dùng phải luôn hiểu rõ chi tiết những gì đã xảy ra chính xác. Tiếp theo this post, việc thực hành tốt nhất làDjango - ngoại lệ xử lý thực hành tốt nhất và gửi thông báo lỗi tùy chỉnh

sử dụng một phản ứng JSON với trạng thái 200 cho phản ứng bình thường của bạn và trả về một (thích hợp!) Phản ứng 4xx/5xx lỗi. Chúng cũng có thể mang theo tải trọng JSON JSON, do đó phía máy chủ của bạn có thể thêm các chi tiết bổ sung về lỗi.

Tôi đã cố gắng google bằng các từ khóa trong câu trả lời này, bởi vẫn có nhiều câu hỏi hơn câu trả lời trong đầu.

  1. Làm cách nào để quyết định mã lỗi nào - 400 hoặc 500 - để trả lại? Tôi có nghĩa là, Django có nhiều loại lỗi được xác định trước, và làm thế nào tôi có thể thực hiện bản đồ này giữa các loại ngoại lệ Django và 400-500 mã lỗi để làm cho các khối xử lý ngoại lệ như DRY và tái sử dụng càng tốt?
  2. Phương pháp tiếp cận với phần mềm trung gian được đề xuất bởi @Reorx trong the post có được coi là khả thi không? (Câu trả lời chỉ có một upvote, do đó làm cho tôi miễn cưỡng để nghiên cứu chi tiết và thực hiện nó trong dự án của tôi
  3. Quan trọng nhất, đôi khi tôi có thể muốn đưa ra lỗi liên quan đến logic nghiệp vụ chứ không phải cú pháp không chính xác hoặc chuẩn Ví dụ: nếu không có CEO trong pháp nhân của tôi, tôi có thể muốn cấm người dùng thêm hợp đồng. Trạng thái lỗi trong trường hợp này là gì và làm cách nào để tôi gửi lỗi giải thích chi tiết về lỗi của mình cho người sử dụng?

chúng ta hãy xem xét nó trên một cái nhìn đơn giản

def test_view (request): 

    try: 
      # Some code .... 
      if my_business_logic_is_violated(): 
       # How do I raise the error 
       error_msg = "You violated bussiness logic because..." 
       # How do I pass error_msg 
      my_response = {'my_field' : value} 
    except ExpectedError as e: 
      # what is the most appropriate way to pass both error status and custom message 
      # How do I list all possible error types here (instead of ExpectedError to make the exception handling block as DRY and reusable as possible 
     return JsonResponse({'status':'false','message':message}, status=500) 

Trả lời

8

Trước hết bạn nên suy nghĩ về những gì sai sót bạn muốn để lộ:

  • Thường 4xx lỗi (lỗi được gán cho client-side) được tiết lộ để người dùng có thể sửa yêu cầu.

  • Ở phía bên kia, lỗi 5xx (Lỗi được quy cho phía máy chủ) thường chỉ được trình bày mà không có thông tin. Theo ý kiến ​​của tôi cho những người bạn nên sử dụng các công cụ như Sentry làm theo dõi và giải quyết lỗi này, có thể có vấn đề an ninh được nhúng trong chúng.

Có ý kiến ​​cho một yêu cầu Ajax chính xác, bạn nên trả về mã trạng thái và sau đó một số lời giúp để hiểu điều gì đã xảy ra như tin nhắn và giải thích (nếu có).

Nếu mục tiêu của bạn là sử dụng ajax để gửi thông tin, tôi khuyên bạn nên đặt form cho những gì bạn muốn. Bằng cách này, bạn sẽ vượt qua một số quy trình xác thực một cách dễ dàng. Tôi sẽ giả định trường hợp này là trong ví dụ.

Đầu tiên - Yêu cầu có chính xác không?

def test_view(request): 
    message = None 
    explanation = None 
    status_code = 500 
    # First, is the request correct? 
    if request.is_ajax() and request.method == "POST": 
     .... 
    else: 
     status_code = 400 
     message = "The request is not valid." 
     # You should log this error because this usually means your front end has a bug. 
     # do you whant to explain anything? 
     explanation = "The server could not accept your request because it was not valid. Please try again and if the error keeps happening get in contact with us." 

    return JsonResponse({'message':message,'explanation':explanation}, status=status_code) 

Second - Có sai sót trong các hình thức?

form = TestForm(request.POST) 
if form.is_valid(): 
    ... 
else: 
    message = "The form has errors" 
    explanation = form.errors.as_data() 
    # Also incorrect request but this time the only flag for you should be that maybe JavaScript validation can be used. 
    status_code = 400 

Bạn thậm chí có thể nhận được trường lỗi theo trường để bạn có thể trình bày theo cách tốt hơn trong biểu mẫu.

Thứ ba - Hãy xử lý yêu cầu

 try: 
      test_method(form.cleaned_data) 
     except `PermissionError` as e: 
      status_code= 403 
      message= "Your account doesn't have permissions to go so far!" 
     except `Conflict` as e: 
      status_code= 409 
      message= "Other user is working in the same information, he got there first" 
     .... 
     else: 
      status_code= 201 
      message= "Object created with success!" 

Tùy thuộc vào trường hợp ngoại lệ bạn xác định, mã khác nhau có thể được yêu cầu. Truy cập Wikipedia và kiểm tra danh sách. Đừng quên rằng phản hồi cũng khác nhau về mã. Nếu bạn thêm một cái gì đó vào cơ sở dữ liệu, bạn nên trả về một 201. Nếu bạn vừa nhận được thông tin thì bạn đang tìm kiếm một yêu cầu GET.

Đáp lại câu hỏi

  1. Django trường hợp ngoại lệ sẽ trở lại 500 lỗi nếu không được giải quyết, bởi vì nếu bạn không biết rằng một ngoại lệ sẽ xảy ra sau đó nó là một lỗi trong máy chủ . Với ngoại lệ cho 404 và yêu cầu đăng nhập, tôi sẽ thực hiện các khối try catch cho mọi thứ. (Đối với 404 bạn có thể tăng nó và nếu bạn làm @login_required hoặc một sự cho phép cần thiết django sẽ trả lời với mã thích hợp mà không cần bạn làm bất cứ điều gì).

  2. Tôi không đồng ý hoàn toàn với cách tiếp cận này. Như bạn đã nói lỗi nên rõ ràng vì vậy bạn nên biết allways những gì được cho là xảy ra và làm thế nào để giải thích nó, và làm cho nó đáng tin cậy vào hoạt động thực hiện.

  3. Tôi sẽ nói lỗi 400 là ok cho điều đó. Đó là một yêu cầu xấu, bạn chỉ cần giải thích lý do tại sao, mã lỗi là dành cho bạn và mã js của bạn vì vậy chỉ cần nhất quán.

  4. (ví dụ được cung cấp) - Trong text_view, bạn nên có test_method như trong ví dụ thứ ba.

phương pháp thử nghiệm nên có cấu trúc sau:

def test_method(validated_data): 
    try: 
     my_business_logic_is_violated(): 
    catch BusinessLogicViolation: 
     raise 
    else: 
     ... #your code 

Các trong ví dụ của tôi:

try: 
     test_method(form.cleaned_data) 
    except `BusinessLogicViolation` as e: 
     status_code= 400 
     message= "You violated the business logic" 
     explanation = e.explanation 
    ... 

tôi coi là vi phạm logic kinh doanh là một lỗi khách hàng vì nếu có điều gì là cần thiết trước yêu cầu đó, khách hàng nên biết điều đó và yêu cầu người dùng thực hiện điều đó trước tiên. (Từ Error Definition):

400 (Bad Request) mã trạng thái chỉ ra rằng máy chủ không hay có thể sẽ không xử lý các yêu cầu do cái gì mà phải nhận thức rõ một lỗi khách hàng (ví dụ, yêu cầu sai cú pháp , yêu cầu không hợp lệ
khung thư hoặc định tuyến yêu cầu lừa đảo).

Nhân tiện, hãy xem Python Docs on User-defined Exceptions để bạn có thể cung cấp thông báo lỗi thích hợp. Ý tưởng đằng sau ví dụ này là bạn nêu ra một ngoại lệ BusinessLogicViolation với một thông báo khác trong my_business_logic_is_violated() theo địa điểm được tạo.

+0

Đây là câu trả lời hay nhất tôi đã xem trên trang web. Cảm ơn bạn rất nhiều vì đã giải thích. Tôi sẽ chọn đánh dấu câu trả lời này là được chấp nhận. Chỉ một yêu cầu nhỏ. Bạn có thể giải thích rõ ràng bằng cách gửi đoạn mã như thế nào vi phạm my_business_logic phù hợp với toàn bộ hình ảnh –

+0

NBajanca, lỗi 428 không được Google Chrome công nhận (mặc dù ok với IE). Bạn có thể vui lòng đề xuất mã lỗi nào cho tôi phù hợp nhất với trường hợp khi bạn muốn tăng lỗi chỉ vì logic nghiệp vụ không chính xác? –

+1

@ EdgarNavasardyan Tôi nghĩ rằng nó phải là một lỗi 400. Tôi không thể tìm thấy một mã tốt hơn để mô tả này và nó rõ ràng là một yêu cầu không hợp lệ từ quan điểm máy chủ của xem. Tôi đã cập nhật câu trả lời để giải thích nó tốt hơn. – NBajanca

2

Các mã trạng thái được xác định rất rõ trong tiêu chuẩn HTTP. Bạn có thể tìm thấy very readable list on Wikipedia. Về cơ bản, các lỗi trong phạm vi 4XX là lỗi do ứng dụng khách thực hiện, nghĩa là nếu chúng yêu cầu tài nguyên không tồn tại, v.v. Các lỗi trong dải 5XX phải được trả lại nếu lỗi gặp phải phía máy chủ.

Đối với điểm số 3, bạn nên chọn lỗi 4XX cho trường hợp điều kiện tiên quyết chưa được đáp ứng, ví dụ 428 Precondition Required, nhưng trả về lỗi 5XX khi máy chủ tăng lỗi cú pháp.

Một trong những vấn đề với ví dụ của bạn là không có phản hồi nào được trả về trừ khi máy chủ đưa ra một ngoại lệ cụ thể, tức là khi mã thực thi bình thường và không có ngoại lệ nào được nêu ra, không phải thông báo cũng như mã trạng thái được gửi đến máy khách một cách rõ ràng . Điều này có thể được xử lý thông qua một khối cuối cùng, để làm cho một phần của mã như là chung nhất có thể.

Theo ví dụ của bạn:

def test_view (request): 
    try: 
     # Some code .... 
     status = 200 
     msg = 'Everything is ok.' 
     if my_business_logic_is_violated(): 
      # Here we're handling client side errors, and hence we return 
      # status codes in the 4XX range 
      status = 428 
      msg = 'You violated bussiness logic because a precondition was not met'. 
    except SomeException as e: 
     # Here, we assume that exceptions raised are because of server 
     # errors and hence we return status codes in the 5XX range 
     status = 500 
     msg = 'Server error, yo' 
    finally: 
     # Here we return the response to the client, regardless of whether 
     # it was created in the try or the except block 
     return JsonResponse({'message': msg}, status=status) 

Tuy nhiên, như đã nêu trong các ý kiến ​​nó sẽ có ý nghĩa hơn để làm cả hai kiểm chứng thực theo cùng một cách, ví dụ:qua trường hợp ngoại lệ, như vậy:

def test_view (request): 
    try: 
     # Some code .... 
     status = 200 
     msg = 'Everything is ok.' 
     if my_business_logic_is_violated(): 
      raise MyPreconditionException() 
    except MyPreconditionException as e: 
     # Here we're handling client side errors, and hence we return 
     # status codes in the 4XX range 
     status = 428 
     msg = 'Precondition not met.' 
    except MyServerException as e: 
     # Here, we assume that exceptions raised are because of server 
     # errors and hence we return status codes in the 5XX range 
     status = 500 
     msg = 'Server error, yo.' 
    finally: 
     # Here we return the response to the client, regardless of whether 
     # it was created in the try or the except block 
     return JsonResponse({'message': msg}, status=status) 
+0

Xem chỉnh sửa của tôi! :) – kreld

+0

'my_business_logic_is_violated()' nên ném một ngoại lệ sẽ được xử lý trong một khối ngoại trừ. – NBajanca

+1

Bạn hiện đang sử dụng chức năng đó như một điều kiện trong một câu nếu - Trong ví dụ này, bạn nên chọn để có nó ném một ngoại lệ, hoặc sử dụng nó như một điều kiện trong một câu nếu, _not_ cả hai. Nếu bạn muốn nó ném một ngoại lệ, chỉ cần di chuyển phần thân của câu nếu vào một khối ngoại lệ mà bắt được ngoại lệ _specific_ rằng hàm tăng – kreld

Các vấn đề liên quan