0

I am attempting to call a RestResource HttpPost method, but receive an INVALID_SESSION_ID response.

[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]

Apex Code (version 43):

@RestResource(urlMapping='/CustomResourceName/*')
global with sharing class CustomResourceClass {

    @HttpGet
    global static string doGet() {
        return 'Success';
    }

    @HttpPost
    global static string doPost() {
        return 'Success';
    }


}

When I call the Get method, "Success" is returned.

My Get call:

var reportUri = string.Format("{0}/services/apexrest/CustomResourceName/{1}", instanceUrl, "ID");

HttpWebRequest http = (HttpWebRequest)WebRequest.Create(reportUri);
http.Method = "GET";
http.ContentType = "application/json";
http.Headers.Add("Authorization", "Bearer " + accessToken);

When I attempt the Post method, I receive the invalid session message.

Post Call:

var reportUri = string.Format("{0}/services/apexrest/CustomResourceName/{1}", instanceUrl, "ID");

HttpWebRequest http = (HttpWebRequest)WebRequest.Create(reportUri);
http.Method = "POST";

var json = "{\"Test\": \"Me\"}";

byte[] postBuffer = Encoding.UTF8.GetBytes(json);
http.ContentLength = postBuffer.Length;
http.ContentType = "application/json";

using (Stream postData = http.GetRequestStream())
{
    postData.Write(postBuffer, 0, postBuffer.Length);
}

http.Headers.Add("Authorization", "Bearer " + accessToken);

I have tried different signatures for the Post call as well:

@HttpPost
global static string doPost(RestRequest req) {
    return 'Success';
}

and

@HttpPost
global static string doPost(string theId, string thePostData) {
    return 'Success';
}

I believe it may be the same as this unanswered question.

EDIT: I was able to get the method to accept my Post by updating the Content Type to "application/text" and changing the post data from Json to a string that is not Json.

Updated Code: var reportUri = string.Format("{0}/services/apexrest/CustomResourceName/{1}", instanceUrl, "ID");

HttpWebRequest http = (HttpWebRequest)WebRequest.Create(reportUri);
http.Method = "POST";

var json = "{\"Test\": \"Me\"}";

byte[] postBuffer = Encoding.UTF8.GetBytes("Json=" + json);
http.ContentLength = postBuffer.Length;
http.ContentType = "application/text";

using (Stream postData = http.GetRequestStream())
{
    postData.Write(postBuffer, 0, postBuffer.Length);
}

http.Headers.Add("Authorization", "Bearer " + accessToken);

How can I make this method work without appending "Json=" to the post body?

  • I answered my initial question of what was wrong, but I would still like to know why it wasn't working. – Brad Ullery Aug 14 '18 at 21:49
1

With application/json it expects you to provide deserializable parameters to the method. What you should do is make a class that represents what you expect to receive in your post:

public class personPost {
    public String name {get; set;}
    public String age {get; set;}

    public personPost(){}
}

public class personResponse {
    public Boolean isSuccess {get; set;}

    public personResponse (boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Then:

// SFDC will automatically deserialize the request into the provided parameter
@HttpPost
global static string doPost(personPost request) {
    insert new Person__c(name = request.name, age__c = request.age);
    return new personResponse(true); // SFDC will automatically convert this to JSON
}

And your request would look like:

'{"name":"Sandra Bullock", "age":"her first pet was a dinosaur"}'

You could theoretically deserialize into an SObject such as contact:

global static string doPost(contact newContact) {
    try {
        insert newContact;
        return '{"id":"' + newContact.Id + '"}';
    } catch(Exception ex) {
        return '{"id":null}';
    }
}

And your request should look like a valid contact:

'{"firstname":"Sandra", "lastname":"Bullock", }'

You can always use JSON.Serialize([SELECT Id, FirstName, LastName FROM Contact LIMIT 1]); In exec anonymous to get an idea of what the request should look like if you're trying to deserialize directly into an SObject.

Here is an answer I like to reference that has an explanation of converting JSON to apex. It's far from perfect but it will hopefully explain a bit more.

Here is another question I answered recently that you might benefit from as well.

0

After a few more trials and plenty of errors I was able to get a working version that makes sense. It doesn't address any ability to use a content type of "application/json", but I was able to post data using content type "x-www-form-urlendcoded".

var reportUri = string.Format("{0}/services/apexrest/CustomResourceName/{1}", instanceUrl, "ID");

HttpWebRequest http = (HttpWebRequest)WebRequest.Create(reportUri);
http.Method = "POST";

http.Headers.Add("Authorization", "Bearer " + accessToken);

var json = "'{\"Test\": \"Me\"}'";

var postBody = "json=" + HttpUtility.UrlEncode(JsonConvert.SerializeObject(json));

byte[] postBuffer = Encoding.UTF8.GetBytes(postBody);
http.ContentLength = postBuffer.Length;
http.ContentType = "application/x-www-form-urlencoded";

using (Stream postData = http.GetRequestStream())
{
    postData.Write(postBuffer, 0, postBuffer.Length);
}

Apex method:

@HttpPost
global static string doPost() {

    MyClass myObject = (MyClass)JSON.deserialize(req.params.get('json'), MyClass.class);

    return 'Success';
}

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.