1

We are using HTTP callout within Salesforce logged in session but when i run the below code it is giving the below error as a response

System.HttpResponse[Status=Unauthorized, StatusCode=401]

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

@future(callout=true) 
public static void doTerritoryCalloutAsync() {
    Http h = new Http();
    HttpRequest req = new HttpRequest();
    req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v30.0/sobjects/UserTerritory');
    req.setBody('{ "TerritoryId" : record , "UserId" : userId}');
    req.setHeader('Authorization','Bearer '+UserInfo.getSessionId());
    req.setHeader('Content-Type', 'application/json');
    req.setMethod('GET');
    try{            
        system.debug('***Request Method Wala***'+req);
        HttpResponse res = h.send(req);
        if(res.getStatusCode() != 200){
            system.debug('user assigned to territory:'+ res.getBody());
        }
        else{
            system.debug('user assigned to territory:'+ res.getBody());
        }
    }
    catch(Exception e){
        system.debug('e.getMessage()');
    }
}
  • 1
    Why are you even using a callout for this functionality? – Adrian Larson Jan 20 '17 at 16:28
0

There are several things wrong with your code, but you are very much using the wrong tool for the job here. Callouts should not be necessary nor preferred.

  • If you want to access specific UserTerritory records, query for them.

    List<UserTerritory> records = [
        SELECT Id FROM UserTerritory
        WHERE TerritoryId = :territoryId
        AND UserId = :userId 
    ];
    
  • If you want to describe the UserTerritory object to access its schema, use describes.

    DescribeSObjectResult describe = SObjectType.UserTerritory;
    

Now, as for things you should fix with your code if you even want to go down this path:

  • Your JSON isn't even valid. Apex isn't going to merge in local variables, if that's what you're hoping will happen there. The simplest route is probably to serialize a Map.

    request.setBody(JSON.serialize(new Map<SObjectField, Id> {
        UserTerritory.TerritoryId => territoryId, UserTerritory.UserId => userId
    }));
    
  • Your catch block needs work. You should never use empty (including debug-only) catch blocks in production code, but even if you do so for testing, at least fix your debug.

    system.debug('e.getMessage()'); // not helpful
    system.debug(e.getMessage()); // better
    
|improve this answer|||||
  • 1
    N.B. although not in the OP, UserTerritory can't be DML'd via Apex so this may have motivated the callout design (adapted from some other use case) – cropredy Jan 20 '17 at 17:35
  • Actually my requirement is to insert UserTerritory record when my user created (Through Apex). I was facing the issue of cannot do DML on UserTerritory and because of that i have to make apex callout to insert the record. If you have any idea doing that, it will be helpful for me to understand. – shashank gupta Jan 22 '17 at 8:26
2

UserInfo.getSessionId does not return a valid session Id when called asynchronously:

For Apex code that is executed asynchronously, such as @future methods, Batch Apex jobs, or scheduled Apex jobs, getSessionId returns null.

As a best practice, ensure that your code handles both cases – when a session ID is or is not available.

Instead, you need to either pass the Session ID from a synchronous context, call a login method to get a fresh Session ID, or use an OAuth flow to get a different Access Token or Refresh Token in order to be able to stay logged in in.

For a future method, the easiest way may simply be to pass in the session as a parameter:

@future(callout=true) 
public static void doTerritoryCalloutAsync(String sessionId) {

Keep in mind that this method is not foolproof; if the user explicitly logs out before you get to use the Session ID, you'll run into this error as well. If you absolutely must have it work, you'll need to arrange for something more permanent like a Refresh Token system.

|improve this answer|||||
  • Hi, can you please help me with how can i achieve this. By using UserInfo.getSessionId() in string and passing the string in the callout? – shashank gupta Jan 22 '17 at 8:29
  • @shashankgupta Yes, something like doTerritoryCalloutAsync(UserInfo.getUserId()). – sfdcfox Jan 22 '17 at 17:17
  • I didn't get you. Do i have to pass userinfo.getuserid() as parameter in method ? – shashank gupta Jan 22 '17 at 17:43

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.