How to correctly return data and errors from REST annotated methods?

Consider this very simple class for querying accounts:

@RestResource(urlMapping='/accounts/*')
global with sharing class AccountRestService {
    @HttpGet
    global static List<Account> getAccounts() {
        List<Account> accounts = [SELECT Id, Name FROM Account];

        return accounts;
    }
}

Easy – I do a GET and get a list of all my accounts.

Now – for some reason this query might fail and we want to correctly signal this to the one using the interface. So in this case we want to return a 500 with the error message as our body.

@RestResource(urlMapping='/accounts/*')
global with sharing class AccountRestService {
    @HttpGet
    global static List<Account> getAccounts() {
        List<Account> accounts;

        try {
            accounts = [SELECT Id, Name FROM Account];
        } catch (Exception e) {
            RestResponse res = RestContext.response;
            res.responseBody = Blob.valueOf(e.getMessage());
            res.statusCode = 500;
        }

        return accounts;
    }
}

This looks nice – however the return statement will overwrite our response in case of an error which is clearly not what we want. Of course you can make it void and do the returning of the data (and the serialization) all by yourself – but that seems a lot of work for something that’s done and should be done by default. Also – all the examples I found so far do have return values but do not handle these cases in any way.

Am I missing something obvious here? Since I never know how my methods will eventually evolve that just leaves me with all void methods since I cannot change the signature of global methods in a managed package.

Answer

Having written a Salesforce managed package app making use of the automatic serialization and deserialization based on the method signature, in future I will avoid that pattern. The primary reason is that it is then impossible to change the format of the data (because of managed package version compatibility constraints) as the OP mentions, but it also limits the error handling choices. (And the return of JSON_PARSER_ERROR errors rather than an opaque error for invalid JSON requests also raised some security review flags.)

I recommend this approach (providing you are happy to stick with JSON):

@RestResource(urlMapping='/accounts/*')
global with sharing class AccountRestService {
    @HttpGet
    global static void getAccounts() {
        RestResponse res = RestContext.response;
        try {
            List<Account> accounts = [SELECT Id, Name FROM Account];
            res.responseBody = Blob.valueOf(JSON.serialize(accounts));
            res.statusCode = 200;
        } catch (Exception e) {
            res.responseBody = Blob.valueOf(e.getMessage());
            res.statusCode = 500;
        }
    }
}

The pain of a few extra lines of code is more than outweighed by the flexibility to change the requests and responses as needed in the future. And it allows a more RESTful style of error handling.

Attribution
Source : Link , Question Author : Semmel , Answer Author : Keith C

Leave a Comment