[SalesForce] Apex Rest – Api error handling HTTP status codes

I am trying to handle multiple error codes in my REST service. Can anyone guide me if I am doing this correctly.

 try {
              if(Limits.getDmlRows() < Limits.getLimitDmlRows()){
              upsert DataUpdate;  
              }
              else if((Limits.getDmlRows() >= Limits.getLimitDmlRows()))
              {
                  RestContext.response.statusCode = 439;
                  ErrorReturn[] err = new ErrorReturn[]{ 
                  new ErrorReturn(DataUpdate.Id, 'Too many DML operations', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                };
                RestContext.response.responseBody = Blob.valueOf(JSON.serialize(err));
                RestContext.response.addHeader('Content-Type', 'application/json');
                break;
              }
            }

            catch(System.CalloutException e){
                System.debug(e.getMessage());
                RestContext.response.statusCode = 403;
                ErrorReturn[] err = new ErrorReturn[]{ 
                    new ErrorReturn(DataUpdate.Id, 'Forbidden', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                };
                RestContext.response.responseBody = Blob.valueOf(JSON.serialize(err));
                RestContext.response.addHeader('Content-Type', 'application/json');
                break;
            }
             catch(System.QueryException e){
                 System.debug(e.getMessage());
                RestContext.response.statusCode = 404;
                ErrorReturn[] err = new ErrorReturn[]{ 
                    new ErrorReturn(DataUpdate.Id, 'ID not found', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                };
                RestContext.response.responseBody = Blob.valueOf(JSON.serialize(err));
                RestContext.response.addHeader('Content-Type', 'application/json');
                break;
             }

             catch(System.StringException e) {
                System.debug(e.getMessage());
                RestContext.response.statusCode = 400;
                ErrorReturn[] err = new ErrorReturn[]{ 
                    new ErrorReturn(DataUpdate.Id, 'Bad Request', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                };
                RestContext.response.responseBody = Blob.valueOf(JSON.serialize(err));
                RestContext.response.addHeader('Content-Type', 'application/json');
                break;
            }

             catch(Exception e) {
              // VALIDATION_EXCEPTION 
                if(e.getMessage().indexOf('FIELD_CUSTOM_VALIDATION_EXCEPTION') >= 0) {
                    RestContext.response.statusCode = 407;
                    ErrorReturn[] err = new ErrorReturn[]{ 
                        new ErrorReturn(DataUpdate.Id, 'Conflict Error', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                };
                } else {
                    RestContext.response.statusCode = 500;
                    ErrorReturn[] err = new ErrorReturn[]{ 
                        new ErrorReturn(DataUpdate.Id, 'Internal Server Error', 'Error updating MessageID ' + msg.MessageId + ': ' + e.getMessage())
                    };
                }
                RestContext.response.responseBody = Blob.valueOf(JSON.serialize(err));
                RestContext.response.addHeader('Content-Type', 'application/json');
                break;
            }

Can some one guide me on how to handle 407, 439 and 403? The caller of my service will either reprocess or discard the record being sent based on the error code returned. I just kept CalloutException as a placeholder for the time being till I find the correct solution.

Thanks

Best Answer

Your question seems to me to approach this issue backwards. You have in hand a list of HTTP status codes and are trying to find Apex exceptions that correspond to ("handle") them.

But there are scores of HTTP status codes, and most of them are either (a) completely irrelevant to this code or (b) handled by the underlying Salesforce application. For example, requests that fail to authenticate, don't deserialize properly (assuming you're letting the platform handle deserialization, as is preferred), or are otherwise nonsensical will receive an HTTP error from Salesforce itself, before your code is invoked and outside your control.

The salient question to me is "what errors can this specific code encounter, and how should they be communicated to the client?" There's only one actual exception the code within your try block can throw and which is catchable: DMLException. You cannot receive CalloutException, QueryException, or StringException. Should a FIELD_CUSTOM_VALIDATION_EXCEPTION message occur, it will be a DMLException, so you do not need separate handling in a generic catch block for it.

Based on your question, the remote caller only needs an answer to one question: should I retry or should I not? DMLException, barring some edge cases, usually is not recoverable and should not be retried without alterations. To me, that's all you need to communicate to the caller, unless there's significant additional complexity you've not shown here. The only route I see that could lead to a retry would be an UNABLE_TO_LOCK_ROW message, which is usually transitory, but I would not build logic for that case unless I knew that it was a likely occurrence.

The only other relevant error appears to be the situation where you have exceeded your DML limits. But unless there is more code with more DML operations above this block, this is impossible: an API call is a single transaction, coming with its associated 150 DML operation limit. You should be able to guarantee that you will not hit this limit within the context of a single API call. (I think you probably wanted 429 Too Many Requests for this error, by the way).

Related Topic