+ Start a Discussion
Jonathan GreenJonathan Green 

Winter '16 broke the Canvas signed request?

Has anyone else experienced this? The Canvas signed request from Salesforce seems to be broken (or changed) with Winter 16, which I haven't found in the release notes.

After Winter '16, our Canvas integration to SAP is broken - from a Summer '15 sandbox, pointing at the exact same SAP URL, everything is still correct, but from Winter '16 orgs, the data comes through with URL encoded values. 

<apex:canvasApp developerName="Canvas_App_Name" height="900px" width="1050px" scrolling="auto"
   parameters="{parm1:'{!Obj__c.Account__c}', parm2:'{!Obj__c.Order_Num__c}'}"
   />
If Account__c is 99999999 and Order_Num__c is 0001234

In Summer '15 this is still received to show parm1 = 99999999 and parm2 = 0001234

In Winter '16, this is received to show parm1 = %3A%229999... and parm2 = %3A%2200012....

Of course, this breaks the functionality on the receiving end, because it's no longer able to understand what the values are. The closest known issue I found was about canvas in communities, which isn't our use case, and is marked as already being fixed in our environments.

 

Best Answer chosen by Jonathan Green
Jonathan GreenJonathan Green
Thanks Gordon, this one is specific to IE, another is specific to communities, we're using neither.

I did find a work around, which I think lends credence to the idea that this is a bug with how Salesforce is parsing the JSON from the parameters attribute of <apex:canvasApp>

Summer '15 solution: 1 line in visualforce:
<apex:canvasApp developerName="Canvas_App_Name" height="900px" width="1050px" scrolling="auto"
   parameters="{p1:'{!Obj__c.Account__c}', p2:'{!Obj__c.Order_Num__c}'}"
   />

Winter '16 solution: 1 line in visualforce, 1 APEX LifecycleHandler class, 1 APEX test class:

In order to pass the parameters correctly in the signed request, it's now necessary to grab what's in the apex:canvasApp parameters attribute, then re-encode the JSON in APEX:

First the VisualForce, change the parameter names to something else - I changed "pram1" and "pram2", which are what our external app expects to "p1" and "p2"
<apex:canvasApp developerName="Canvas_App_Name" height="900px" width="1050px" scrolling="auto"
   parameters="{p1:'{!Obj__c.Account__c}', p2:'{!Obj__c.Order_Num__c}'}"
   />
and the APEX, where we set the parameters "pram1" and "pram2" again. 
public with sharing class LifecycleHandlerClassName implements Canvas.CanvasLifecycleHandler {
	public Set<Canvas.ContextTypeEnum> excludeContextTypes(){
        return new Set<Canvas.ContextTypeEnum>();
    }
	
	public void onRender(Canvas.RenderContext renderContext) {
        Canvas.EnvironmentContext env = renderContext.getEnvironmentContext();
        Map<String, Object> currentParams = ( Map<String, Object> ) JSON.deserializeUntyped(env.getParametersAsJSON());
        env.setParametersAsJSON('{"parm1":"' + currentParams.get('p1') + '","parm2":"' + currentParams.get('p2') + '"}');
    }
}
Of course, this now requires APEX test coverage:
@isTest
global class LifecycleHandler_Test {
    static testMethod void LifecycleHandlerOrderParts(){
        // Test handler with some mock environment context values
        LifecycleHandlerOrderParts handler = new LifecycleHandlerOrderParts();
        handler.excludeContextTypes();
        Canvas.RenderContext mock = Canvas.Test.mockRenderContext(null,null);
        // Directly update the mock RenderContext and set the custom parameters
        mock.getEnvironmentContext().setParametersAsJSON('{\"p1\":1,\"p2\":2}');
        Canvas.Test.testCanvasLifecycle(handler,mock);
    }
}

 

All Answers

Gordon EngelGordon Engel
Is it related to this Known Issue?

https://success.salesforce.com/issues_view?id=a1p30000000TL1NAAW
 
Jonathan GreenJonathan Green
Thanks Gordon, this one is specific to IE, another is specific to communities, we're using neither.

I did find a work around, which I think lends credence to the idea that this is a bug with how Salesforce is parsing the JSON from the parameters attribute of <apex:canvasApp>

Summer '15 solution: 1 line in visualforce:
<apex:canvasApp developerName="Canvas_App_Name" height="900px" width="1050px" scrolling="auto"
   parameters="{p1:'{!Obj__c.Account__c}', p2:'{!Obj__c.Order_Num__c}'}"
   />

Winter '16 solution: 1 line in visualforce, 1 APEX LifecycleHandler class, 1 APEX test class:

In order to pass the parameters correctly in the signed request, it's now necessary to grab what's in the apex:canvasApp parameters attribute, then re-encode the JSON in APEX:

First the VisualForce, change the parameter names to something else - I changed "pram1" and "pram2", which are what our external app expects to "p1" and "p2"
<apex:canvasApp developerName="Canvas_App_Name" height="900px" width="1050px" scrolling="auto"
   parameters="{p1:'{!Obj__c.Account__c}', p2:'{!Obj__c.Order_Num__c}'}"
   />
and the APEX, where we set the parameters "pram1" and "pram2" again. 
public with sharing class LifecycleHandlerClassName implements Canvas.CanvasLifecycleHandler {
	public Set<Canvas.ContextTypeEnum> excludeContextTypes(){
        return new Set<Canvas.ContextTypeEnum>();
    }
	
	public void onRender(Canvas.RenderContext renderContext) {
        Canvas.EnvironmentContext env = renderContext.getEnvironmentContext();
        Map<String, Object> currentParams = ( Map<String, Object> ) JSON.deserializeUntyped(env.getParametersAsJSON());
        env.setParametersAsJSON('{"parm1":"' + currentParams.get('p1') + '","parm2":"' + currentParams.get('p2') + '"}');
    }
}
Of course, this now requires APEX test coverage:
@isTest
global class LifecycleHandler_Test {
    static testMethod void LifecycleHandlerOrderParts(){
        // Test handler with some mock environment context values
        LifecycleHandlerOrderParts handler = new LifecycleHandlerOrderParts();
        handler.excludeContextTypes();
        Canvas.RenderContext mock = Canvas.Test.mockRenderContext(null,null);
        // Directly update the mock RenderContext and set the custom parameters
        mock.getEnvironmentContext().setParametersAsJSON('{\"p1\":1,\"p2\":2}');
        Canvas.Test.testCanvasLifecycle(handler,mock);
    }
}

 
This was selected as the best answer
Ivaylo BalabanovIvaylo Balabanov
Hello,

Yes, it is connect but it does not work with any of the browsers not only with IE.
It says that is fixed but it is not.
Regards,
Ivo