Call Salesforce Visualforce from ExternalSite

皆さん、おはようございます。 本日、外部からセールフォースAPI(自作API)へのアクセスCross-Originの問題に解消方法をご紹介させて頂きます。 我々はよくForce.comサイツでAPI構築したら外部へ公開・アクセス許可しておりますが最近提供していたAPIへのアクセスはブロックされてしまって泣きました。 外部からAjaxでAPIを呼び出した時以下のエラーが出てきて期待するレスポンスは返さないようです。

Access to XMLHttpRequest at 'https://cafe-ti-amo-developer-edition.ap0.force.com/ApexJson' from origin 'https://myevent-dev-ed--caps.ap0.visual.force.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

APIサンプル

  1. Apexソース
public class ApexJSON {
    public ApexJSON() {
        //ApexPages.currentPage().getHeaders().put('Access-Control-Allow-Origin','*');
    }
    public  String getContactIdsAsJson() {        
        //
        //ApexPages.currentPage().getHeaders().put('Content-Type','application/json');
        List<Contact> contacts = [SELECT Id From Contact LIMIT 10];

        List<Id> ids = new List<Id>();
        for (Contact c : contacts) {
            ids.add(c.Id);
        }

        Map<String, Object> obj = new Map<String, Object>();
        obj.put('contactIds', ids);
        return JSON.serialize(obj);
    }
}
  1. Visualforceソース

<apex:page controller="ApexJSON" contentType="application/x-JavaScript; charset=utf-8" showHeader="false" standardStylesheets="false" sidebar="false">
  {!contactIdsAsJson}
</apex:page>
  1. Visualforceから期待するレスポンス(JSON形)
{"contactIds":["0031000000iv3r4AAA","0031000000iv3r5AAA","0031000000iv3r6AAA","0031000000iv3r9AAA","0031000000iv3rAAAQ","0031000000iv3rBAAQ","0031000000iv3rCAAQ","0031000000iv3rDAAQ","0031000000iv3rEAAQ","0031000000iv3rFAAQ"]}
  1. アクセス元のソース(Ajax)
$j.ajax({
     type: "GET",
     url: 'https://cafe-ti-amo-developer-edition.ap0.force.com/ApexJson', // outside Domain
     headers: {"Accept" : "application/json",
                "Content-Type": "application/json" },
     crossDomain : true,
     dataType: 'json',
     success: function (responseData) {
        console.log(responseData);

    },
    error: function (request, status, error) {
        console.log(request.responseText);
        console.log(status);
        console.log(error);
        var result = $j("#selections").append("Sorry, Something in the system has gone wrong , Please try again Later");
        console.log("Sorry, Something in the system has gone wrong , Please try again Later");
    }

});

なぜだろう、作った時ちゃんとセールフォースのCORSにリクエスト元のURLパターンを登録しましたが なぜ最近、こういうエラーが出るの? エラーメッセージを確認する限りAPIリクエスト前に preflightリクエストはダメらしいでした。 そのためアクセス元のOriginは不明だったので アクセス先は許可しないようになっています。

  1. 対策:やっぱりアクセス先のサーバーサイドに リクエストヘッダーにAccess-Control-Allow-Originを入れ込まないといけないようです。

Access-Control-Allow-Originを追加した後Apexクラス(コンストラクターに追加)

public class ApexJSON {
    public ApexJSON() {
        //リクエストヘッダーにオーバーライド
        ApexPages.currentPage().getHeaders().put('Access-Control-Allow-Origin','*');
    }
    public  String getContactIdsAsJson() {        
        //
        //ApexPages.currentPage().getHeaders().put('Content-Type','application/json');
        List<Contact> contacts = [SELECT Id From Contact LIMIT 10];

        List<Id> ids = new List<Id>();
        for (Contact c : contacts) {
            ids.add(c.Id);
        }

        Map<String, Object> obj = new Map<String, Object>();
        obj.put('contactIds', ids);
        return JSON.serialize(obj);
    }
}

再度、リクエスト元から試すと別のエラーになりました。おっと

Access to XMLHttpRequest at 'https://cafe-ti-amo-developer-edition.ap0.force.com/ApexJson' from origin 'https://myevent-dev-ed--caps.ap0.visual.force.com' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

おーおーエラー内容をみる限りリクエスト元に「content-type」属性を含めてはいけないみたいです。 ではアクセス元より「content-type」を外してみよう。 もう一度試すと。。。。結果として期待値をちゃんと返しました。やったぜー

{contactIds: Array(10)}
contactIds: Array(10)
0: "0031000000iv3r4AAA"
1: "0031000000iv3r5AAA"
2: "0031000000iv3r6AAA"
3: "0031000000iv3r9AAA"
4: "0031000000iv3rAAAQ"
5: "0031000000iv3rBAAQ"
6: "0031000000iv3rCAAQ"
7: "0031000000iv3rDAAQ"
8: "0031000000iv3rEAAQ"
9: "0031000000iv3rFAAQ"
length: 10
__proto__: Array(0)
__proto__: Object

Happy Coding!

Posted in Apex, Salesforce, Vietnam Offshore on Jan 22, 2019