8

I have a question about the urlmapping property on a REST class. For the moment I have 3 REST classes with following urlmappings:

  1. REST1.cls @RestResource(urlmapping='/invoices/*/')
  2. REST2.cls @RestResource(urlmapping='/invoices/*/totals/')
  3. REST3.cls @RestResource(urlmapping='/invoices/*/related/')

I have 3 types of corresponding REST urls:

  1. "/services/apexrest/invoices/due/" or "/services/apexrest/invoices/paid/"
  2. "/services/apexrest/invoices/{Salesforce Id}/totals/"
  3. "/services/apexrest/invoices/{Salesforce Id}/related/"

When I do the requests I get the following results:

  • CORRECT "/services/apexrest/invoices/due/" maps to REST1.cls
  • CORRECT "/services/apexrest/invoices/paid/" maps to REST1.cls
  • INCORRECT "/services/apexrest/invoices/{Salesforce Id}/totals/" maps to REST1.cls (should map to REST2.cls)
  • INCORRECT "/services/apexrest/invoices/{Salesforce Id}/related/" maps to REST1.cls (should map to REST3.cls)

When I look at the documentation I see that these are the rules followed:

  • An exact match always wins.
  • If no exact match is found, find all the patterns with wildcards that match, and then select the longest (by string length) of those.
  • If no wildcard match is found, an HTTP response status code 404 is returned.

I would expect they map to the classes listed above. Can someone explain what is going wrong here?

UPDATE: I want to keep the logic of my REST url's, the sequence of the parameters and url structure is in line with usability best practises, and I believe the salesforce url mapping should be capable of this.

7
+50

EDITED: It works sometimes without any problem (even with the oldest Api 22.0 that supports RestResource) and sometimes incorrectly.

That depends on the order of classes sorted by the time of the last modification.

If you edit the class REST1.cls or generally that with the shortest urlMapping length (add a comment into) it starts to work as you expect.

It is not exactly incorrect, because * matches any string with any number of /. It seems that the text "the patterns with wildcards... select the longest (by string length)" should be understand only "the longest string before the wildcard". The order of compared matching is not explicitely defined by docs if these parts are the same. They mean only: /word/* is a longer and better match than /*.

They want to provide a fast service. Therefore they probably need firstly to do a coarse match by an index, then to try classes ordered by this index until the first match is found. If you can find a good index expression than it can be probably fixed, but it is much complicated by rules about the final slash. I'm skeptical of anything, except for asking a possible very small improvement in documentation.

Tested code exemples:
REST1

@RestResource(urlmapping='/invoices/*/')
global class REST1 {
    @HttpGet
    global static String getClassName() {
        return 'REST1';
    }
}

REST2

@RestResource(urlmapping='/invoices/*/totals/')
global class REST2 {
    @HttpGet
    global static String getClassName() {
        return 'REST2';
    }
}

REST3

@RestResource(urlmapping='/invoices/*/related/')
global class REST3 {
    @HttpGet
    global static String getClassName() {
        return 'REST3';
    }
}

It is better and more universal to use urlMapping string without the final slash, but is is not important for this question. Example:

@RestResource(urlmapping='/invoices/*')

It can catch both /invoices/something/ and /invoices/something, while the mapping with the final slash requires only this one. Every example in Apex docs (e.g. the qouted above) is without any final slash.

**EDITED ** because the problem was not initially reproducible.

  • 1
    Removing the trailing slashes does not solve the problem. I also tested REST2 and REST3 by removing one and then the other. They both work on their own but not together. I just need to find out how the internal matching works so I can understand what I can do and what not. But thanks! – stvnhg May 20 '14 at 13:32
  • @stvnhg Yes. It behaves randomly. I still know how. – hynekcer May 20 '14 at 20:24
  • @SamuelDeRycke: I edited my answer for the last time, about the background of question, in the 4th and 5th paragraph. – hynekcer May 21 '14 at 14:29
2

The documentation is not clear but I believe that it will match

urlmapping='/invoices/*/

over

urlmapping='/invoices/*/totals/

This is because the wildcard character is not at the end. To fix this issue, you might have to vary your url format for REST2 and 3 to the following.

urlmapping='/invoices/totals/*

urlmapping='/invoices/related/*

This means that your url will be:

/invoices/totals/{salesforceId}

/invoices/related/{salesforceId}

  • This will probably work but then I would need to change the logic in my rest url's, which I do not want to do. What you are suggesting are not discoverable url's. Can you explain the internal matching? Thanks! – stvnhg May 12 '14 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.