Skip to main content
@RestResource(urlMapping='/my/endpoint/*')
global class MyRestAPI{
    // You could use an if/else if chain or a switch statement to take care of this,
    //   but I personally find using a Map to be more elegant
    private static Map<String, iRestPostHandler> resourceToHandler = new Map<String, iRestPostHandler>{
        '/my/endpoint/service1' => new Service1Handler(),
        '/my/endpoint/service2' => new Service2Handler()
    };

    @HttpPost
    global static void dispatch(){
        // @Http<X> annotated methods have access to the RestContext variable, which
        //   contains a RestRequest instance
        // The RestRequest contains the request URI, which will always start with the
        //   urlMapping defined in the @RestResource annotation

        if(resourceToHandler.containsKey(RestContext.request.requestURI)){
            // dispatch to the appropriate class
            resourceToHandler.get(RestContext.request.requestURI).handleRequest(RestContext.request, RestContext.response);
        }
    }

    public interface iRestPostHandler{
        // Have the interface take both the request (so you can handle the request body
        //   separately in each concrete class) and response (so that the dispatcher
        //   doesn't need to worry about handling output)
        public void handleRequest(RestRequest req, RestResponse resp)
    }

    // Because we don't need to use the @HttpPost annotation (not like we could), we
    //   can make use of inner classes
    public class Service1Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);
            //do work
        }
    }

    public class Service2Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);

            //do different work
        }
    }
}
@RestResource(urlMapping='/my/endpoint/*')
global class MyRestAPI{
    // You could use an if/else if chain or a switch statement to take care of this,
    //   but I personally find using a Map to be more elegant
    private static Map<String, iRestPostHandler> resourceToHandler = new Map<String, iRestPostHandler>{
        '/my/endpoint/service1' => new Service1Handler(),
        '/my/endpoint/service2' => new Service2Handler()
    }

    @HttpPost
    global static void dispatch(){
        // @Http<X> annotated methods have access to the RestContext variable, which
        //   contains a RestRequest instance
        // The RestRequest contains the request URI, which will always start with the
        //   urlMapping defined in the @RestResource annotation

        if(resourceToHandler.containsKey(RestContext.request.requestURI)){
            // dispatch to the appropriate class
            resourceToHandler.get(RestContext.request.requestURI).handleRequest(RestContext.request, RestContext.response);
        }
    }

    public interface iRestPostHandler{
        // Have the interface take both the request (so you can handle the request body
        //   separately in each concrete class) and response (so that the dispatcher
        //   doesn't need to worry about handling output)
        public void handleRequest(RestRequest req, RestResponse resp)
    }

    // Because we don't need to use the @HttpPost annotation (not like we could), we
    //   can make use of inner classes
    public class Service1Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);
            //do work
        }
    }

    public class Service2Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);

            //do different work
        }
    }
}
@RestResource(urlMapping='/my/endpoint/*')
global class MyRestAPI{
    // You could use an if/else if chain or a switch statement to take care of this,
    //   but I personally find using a Map to be more elegant
    private static Map<String, iRestPostHandler> resourceToHandler = new Map<String, iRestPostHandler>{
        '/my/endpoint/service1' => new Service1Handler(),
        '/my/endpoint/service2' => new Service2Handler()
    };

    @HttpPost
    global static void dispatch(){
        // @Http<X> annotated methods have access to the RestContext variable, which
        //   contains a RestRequest instance
        // The RestRequest contains the request URI, which will always start with the
        //   urlMapping defined in the @RestResource annotation

        if(resourceToHandler.containsKey(RestContext.request.requestURI)){
            // dispatch to the appropriate class
            resourceToHandler.get(RestContext.request.requestURI).handleRequest(RestContext.request, RestContext.response);
        }
    }

    public interface iRestPostHandler{
        // Have the interface take both the request (so you can handle the request body
        //   separately in each concrete class) and response (so that the dispatcher
        //   doesn't need to worry about handling output)
        void handleRequest(RestRequest req, RestResponse resp)
    }

    // Because we don't need to use the @HttpPost annotation (not like we could), we
    //   can make use of inner classes
    public class Service1Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);
            //do work
        }
    }

    public class Service2Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);

            //do different work
        }
    }
}
Source Link
Derek F
  • 64k
  • 15
  • 54
  • 104

The URL that you use to access a custom apex rest service is determined by the @RestResource annotation, which can only be used at the class level.

So it makes sense for Salesforce to only allow a single @HttpPost method in a @RestResource annotated class. There would be no way to provide a more specific URL to be able to choose between two @HttpPost methods. The @HttpPost method also needs to be static, so that precludes using inner classes to help juggle things.

What you can do, however, is have your @HttpPost method act as a receptionist. Your @HttpPost method stands at the front door and interacts with all incoming requests. You can then have several other methods (or other classes) that your @HttpPost receptionist dispatches the incoming request to.

Something like

@RestResource(urlMapping='/my/endpoint/*')
global class MyRestAPI{
    // You could use an if/else if chain or a switch statement to take care of this,
    //   but I personally find using a Map to be more elegant
    private static Map<String, iRestPostHandler> resourceToHandler = new Map<String, iRestPostHandler>{
        '/my/endpoint/service1' => new Service1Handler(),
        '/my/endpoint/service2' => new Service2Handler()
    }

    @HttpPost
    global static void dispatch(){
        // @Http<X> annotated methods have access to the RestContext variable, which
        //   contains a RestRequest instance
        // The RestRequest contains the request URI, which will always start with the
        //   urlMapping defined in the @RestResource annotation

        if(resourceToHandler.containsKey(RestContext.request.requestURI)){
            // dispatch to the appropriate class
            resourceToHandler.get(RestContext.request.requestURI).handleRequest(RestContext.request, RestContext.response);
        }
    }

    public interface iRestPostHandler{
        // Have the interface take both the request (so you can handle the request body
        //   separately in each concrete class) and response (so that the dispatcher
        //   doesn't need to worry about handling output)
        public void handleRequest(RestRequest req, RestResponse resp)
    }

    // Because we don't need to use the @HttpPost annotation (not like we could), we
    //   can make use of inner classes
    public class Service1Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);
            //do work
        }
    }

    public class Service2Handler implements iRestPostHandler{
        public void handleRequest(RestRequest req, RestResponse resp){
            Map<String, Object> requestBodyDeserialized = JSON.deserializeUntyped(req.requestBody);

            //do different work
        }
    }
}