2016-08-09 50 views
8

我開始考慮在我的Django應用程序中進行適當的異常處理,我的目標是儘可能方便用戶。通過用戶友好性,我暗示用戶必須始終詳細說明究竟發生了什麼問題。 繼this post,最好的做法是Django - 異常處理最佳做法併發送自定義錯誤消息

使用狀態爲200的JSON響應爲您的正常反應和 返回錯誤的(在適當的!)的4xx/5xx響應。這些還可以攜帶 JSON有效負載,因此您的服務器端可以添加有關該錯誤的其他詳細信息 。

我試着谷歌的關鍵詞在這個答案,仍然有更多的問題比我腦海中的答案。

  1. 如何決定返回哪個錯誤代碼 - 400或500?我的意思是,Django有許多預定義的錯誤類型,我如何在Django異常類型和400-500錯誤代碼之間實現這種映射,以使異常處理塊儘可能乾和可重用?
  2. @Reorx建議的中間件the post的方法是否可行? (答案只有一個upvote,因此我不願意深入細節並在我的項目中實現它
  3. 最重要的是,有時我可能想提出一個與業務邏輯有關的錯誤,而不是錯誤的語法或像null這樣的標準例如,如果我的法律實體中沒有CEO,我可能想要禁止用戶添加合同。在這種情況下,錯誤狀態應該是什麼,以及如何在錯誤中詳細解釋錯誤用戶?

我們認爲這是一個簡單的觀點

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) 

回答

8

所有你應該想想你要揭露什麼錯誤首先:

  • 通常4XX錯誤(這歸因於客戶端錯誤)被公開所以用戶可以糾正請求。

  • 另一方面,5xx錯誤(歸因於服務器端的錯誤)通常只會在沒有信息的情況下顯示。在我看來,對於那些你應該使用像Sentry這樣的工具來做監視和解決這個錯誤,這可能會有安全問題嵌入其中。

有了這個主意是我一個正確的Ajax意見請求應該返回一個狀態代碼,然後一些JSON來幫助理解發生了什麼樣的消息,並解釋(如適用)。

如果你的目標是使用Ajax提交信息,我建議你設置一個form。通過這種方式,您可以輕鬆過去一些驗證過程。我將假設案例是這個例子。

第一個 - 請求是否正確?

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) 

- 是否有形式的錯誤?

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 

你甚至可能會得到錯誤字段的字段,所以你可能會以更好的方式在表單中呈現。

- 讓我們來處理請求

 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!" 

根據您定義的例外,可能需要不同的代碼。去Wikipedia並檢查列表。 不要忘記,代碼中的響應也有所不同。如果您向數據庫添加某些東西,則應返回201。如果你剛獲得信息,那麼你正在尋找一個GET請求。

回答問題時

  1. Django的異常將返回500錯誤,如果沒有處理,因爲如果你不知道的異常會再發生是在服務器錯誤。除404和登錄要求外,我會爲所有事情做try catch塊。 (對於404你可能會提出,如果你做@login_required或需要權限,django會用適當的代碼作出迴應,而你沒有做任何事情)。

  2. 我完全不同意這種方法。正如你所說的,錯誤應該是明確的,所以你應該知道總是會發生什麼以及如何解釋它,並使它可以依賴於所執行的操作。

  3. 我會說400錯誤是可以的。這是一個錯誤的請求,你只需要解釋爲什麼,錯誤代碼適合你和你的js代碼,所以只需要保持一致。

  4. (提供示例) - 在text_view中,應該有第三個示例中的test_method

試驗方法應具有以下結構:

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

的在我的例子:

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

我認爲業務邏輯違規是一個客戶端錯誤,因爲如果需要的東西在此請求之前,客戶端應該意識到這一點並要求用戶先做。 (從Error Definition):

400(錯誤請求)狀態代碼表示該服務器不能或 不會請求過程中由於一些被認爲是 客戶端錯誤(例如,惡意請求語法,無效請求
消息框架或欺騙性請求路由)。

順便說一句,請參閱Python Docs on User-defined Exceptions,以便您可以給出相應的錯誤消息。此示例背後的想法是,根據生成的地方,您在my_business_logic_is_violated()中使用不同的消息提出BusinessLogicViolation例外。

+0

這是我在網站上看到的最佳答案。非常感謝你的解釋。我會選擇打勾接受這個答案。只是一個小問題。您能否通過提交代碼片段來明確解釋my_business_logic違規如何適合整個圖片 –

+0

NBajanca,Google Chrome無法識別428錯誤(雖然可以使用IE)。您能否請您建議哪種錯誤代碼最適合當您想因爲業務邏輯不正確而引發錯誤時的情況? –

+1

@EdgarNavasardyan我認爲它應該是一個錯誤400.我找不到一個更好的代碼來描述這一點,它顯然是從服務器的角度來看無效的請求。我更新了答案以更好地解釋它。 – NBajanca

2

狀態碼在HTTP標準中定義得很好。你可以找到一個very readable list on Wikipedia。基本上,4XX範圍內的錯誤是由客戶端產生的錯誤,即如果他們請求不存在的資源等。如果在服務器端遇到錯誤,則應該返回5XX範圍內的錯誤。

關於第3點,如果前提條件未得到滿足,例如428 Precondition Required,則應選擇4XX錯誤,但在服務器發出語法錯誤時返回5XX錯誤。

您的示例的問題之一是,除非服務器引發特定的異常,即代碼正常執行且不會引發異常,否則不會將消息或狀態代碼明確發送到客戶端。這可以通過finally塊來處理,以使代碼的這部分儘可能通用。

按照您的例子:

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) 

然而,正如在評論中指出,它會更有意義做到既驗證了同樣的方式,即通過例外,像這樣:

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

看我的編輯! :) – kreld

+0

'my_business_logic_is_violated()'應該拋出一個將在except塊中處理的異常。 – NBajanca

+1

您目前正在使用該函數作爲if語句中的條件 - 在本例中,您應該選擇讓它拋出異常,或者將其用作if語句中的條件,而不是同時使用它們。如果你想讓它拋出一個異常,只要將if語句的主體移動到一個異常塊中,捕獲該函數引發的_specific_異常 – kreld

相關問題