An @RestResource Apex class that returns multiple JSON formats

The simplest way to write an @RestResource class is to return Apex objects from the @Http methods and leave it up to the platform to serialize these objects as JSON (or XML):

@RestResource(urlMapping='/report/*')
global without sharing class ReportRest {

    public class MyInnerClass {
        public String name;
        public Integer number;
    }

    @HttpGet  
    global static MyInnerClass get() {
        MyInnerClass instance = new MyInnerClass();
        ...
        return instance;
    }
}

This also allows tests to be written that don’t have to deserialize as they can just reference the class instances directly. But the approach imposes these limitations:

  • The response JSON is fixed and determined by the returned classes and their fields so responses that vary depending on the URL requested can’t be produced
  • Error conditions typically get handled by adding error fields to the response object rather than by returning a status code other than 200 and separate error information

Here is an alternate pattern that is a bit more work but in my experience meets the needs of client-side MVC applications (AngularJS in my case) better. The class returns two different JSON formats (depending on the part of the URL after “/report/”):

@RestResource(urlMapping='/report/*')
global without sharing class ReportRest {

    public class Day {
        public Date date;
        public Integer hours;
    }
    
    public class Employee {
        public String name;
        public Day[] approved = new Day[] {};
    }
    
    public class Claim {
        public String employeeName;
        public String claimNumber;
    }
 
    @HttpGet  
    global static void get() {
        RestResponse res = RestContext.response;
        if (res == null) {
            res = new RestResponse();
            RestContext.response = res;
        }
        try {
            res.responseBody = Blob.valueOf(JSON.serialize(doGet(extractReportId())));
            res.statusCode = 200;
        } catch (EndUserMessageException e) {
            res.responseBody = Blob.valueOf(e.getMessage());
            res.statusCode = 400;
        } catch (Exception e) {
            res.responseBody = Blob.valueOf(
                    String.valueOf(e) + '\n\n' + e.getStackTraceString()
                    );
            res.statusCode = 500;
        }
    }
    
    private static Object doGet(String reportId) {
        if (reportId == 'ac') {
            return absenceCalendarReport();
        } else if (reportId == 'al') {
            return absenceListReport();
        } else if (reportId == 'dl') {
            return disabilityListReport();
        } else {
            throw new EndUserMessageException(reportId + ' not implemented');
        }
    }
    
    private static Employee[] absenceCalendarReport() {
        Employee[] employees = new Employee[] {};
        ...
        return employees;
    }
    
    private static Claim[] absenceListReport() {
        Claim[] claims = new Claim[] {};
        ...
        return claims;
    }
    
    private static Claim[] disabilityListReport() {
        Claim[] claims = new Claim[] {};
        ...
        return claims;
    }
    
    private static String extractReportId() {
        String[] parts = RestContext.request.requestURI.split('\\/');
        String lastPart = parts[parts.size() - 1];
        Integer index = lastPart.indexOf('?');
        return index != -1 ? lastPart.substring(0, index) : lastPart;
    }
}

Apex classes are still used to represent the returned data but are explicitly serialized using a JSON.serialize call. As the overall response is being explicitly built, the returned status code can be set allowing the client side to vary its logic depending on that status code. In this example error information – intended to be shown to an end user as signalled by the EndUserMessageException custom exception or unintended and so including a stack trace – is returned as plain text that can be directly shown to the end user.

Advertisements