1

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?

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

2 Answers 2

2

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';
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .