[SalesForce] Handing REST API POST Exceptions using SSJS Try-Catch on a Landing Page Form Submission with AMPSCRIPT

I have an issue with a landing page containing a webform that submits input from the user and using AMPSCRIPT I make a Rest API call using the HTTPPost2 function to utilize the validateEmail resource. I know that SSJS also has HTTPPost2 function but I want to use AMPSCRIPT version to perform this action and use SSJS's Try Catch to handle exceptions.

I use an api that requires requesting a new access token every 60 minutes because the old one expires. I have ampscript lookup logic in place along with a data extension that aids in both retrieving an existing AccessToken and/or updating the value once the 60 minute max has been exceeded. There is no issue when the AccessToken is active and created within the last 60 minutes. However, When the error exception occurs that's when request for a new AccessToken needs to take place and then attempt to perform and update the DE with the new AccessToken. Finally, the AccessToken is retrieve that AccessToken DE to use to perform the initial API request.

Upon the form submission and API call the observation that I'm seeing is that the try-catch appears to work but only after the 2nd try. After I submit the form on the first attempt the API call results in a 401 – Unauthorized error and I see the "An unexpected error has occurred!" and ofcourse with a custom RaiseError message RaiseError(CONCAT('The error experienced was: ',@httppost),false), I confirmed that it is the 401 Unauthorized.

When I implement the SSJS Try-Catch by wrapping it around my AMPSCRIPT logic, I expected it catch the exception the first time and allow the 2nd API call to proceed in the exception block provided that I caught the exception accurately. I tested the regex function and found that this works well.
However, it only seems to proceed to this point ONLY AFTER I reload the browser upon seeing the 'Unauthorized Error message' and re-try the submission and I am unable to determine why. Btw, the custom RaiseError is just there to show what the API error code is. When I remove that line it still shows me the generic error message that an error has occurred which when I reload the page and try resubmitting it is able to catch the 401 error and move on to execute the exception block which performs the desired action.

I would greatly appreciate it if anyone can detect and offer a fix to what is preventing the 401 error code exception from being processed and evaluated on the first attempt at a form submission when the AccessToken is expired.

I will post my source code void of any of my credentials and without any client JS form validation.

Submission Form

<form action="%%=RequestParameter('PAGEURL')=%%" id="subscribeForm" 
   name="subscribeForm" method="post">
   <table cellpadding="0" cellspacing="0" border="0">
     <tr>
        <td height="60" width="250">
         <font face="Verdana" size="2">Email</font>*<br />
         <input type="text" id="EmailAddress" name="EmailAddress"></td>
     </tr>
     <tr>
        <td height="60" colspan="2">
        <input name="Submit" class="submit" type="submit" value="Submit"> 
        </td> 
        <td></td>
     </tr>
   </table>
</form>

Below is the logic (alternating between Serverside JavaScript (SSJS) AMPSCRIPT blocks)

SSJS:

<script language="javascript" runat="server"> 
     Platform.Load("Core","1");  
     try{ 
</script>

Ampscript:

%%[ var @subscriberKey, @emailAddress, @url, @body, 
     @httppost,@apiid,@apistatusCode,@apiresponse,@apitoken, 
     @accessToken, @now, @callstatus, @response, @errorMsg

     set @emailAddress = ""
     set @httppost = ""

     if RequestParameter("Submit") == "Submit" then

       set @emailAddress = RequestParameter("EmailAddress")

       set @body = CONCAT('{"email": ', CONCAT('"', @emailAddress, 
       '",'),'"validators": ', '["SyntaxValidator",', '"MXValidator",', 
       '"ListDetectiveValidator"',']}')

       set @apitoken = Lookup('AccessToken','AccessToken','id',1)
       set @accessToken = CONCAT('BEARER ', @apitoken)

       set @url = "https://www.exacttargetapis.com/address/v1/validateEmail"

       set @httppost = HTTPPost2(@url, "application/json", @body, true, 
       @callstatus, @response, 'Authorization', @accessToken)

       RaiseError(CONCAT('The error experienced was: ',@httppost),false)

     endif
]%%

SSJS:

<script language="javascript" runat="server">
} catch (ex) {
     Variable.SetValue("@errorMsg", Stringify(ex))
</script>

AMPSCRIPT:

%%[

     if RequestParameter("Submit") == "Submit" then

     var @regEx1, @resultOfRegex1

     set @errorMsg = Substring(@errorMsg,2, Subtract(Length(@errorMsg),2))

     set @regEx1 = '(401).+(Unauthorized)'

     set @resultOfRegex1 = RegExMatch(@errorMsg, @regEx1,  0)

     if Length(@resultOfRegex1) > 0 THEN

     set @now = Now()

     /* part A - Refresh AccessToken */

     set @apiid = '{"clientId": "MYCLIENTIDGOESHERE",
                    "clientSecret":"MYCLIENTSECRETGOESHERE"}'
     set @httppost = 
     HTTPPost2("https://auth.exacttargetapis.com/v1/requestToken",
     "application/json", @apiid,false,@apistatusCode,@apiresponse)
     set @apitoken = Substring(@apistatusCode,17,24)
     set @accessToken = CONCAT('BEARER ', @apitoken)

     set @httppost = HTTPPost2(@url, "application/json", 
     @body, true, @callstatus, @response, 'Authorization', @accessToken)

     UpsertData("AccessToken",1,"id","1","AccessToken",@apitoken)
     UpsertData("AccessToken",1,"id","1","Last Updated",@now)

     /* part B - Validate EmailAddress */

     set @body = CONCAT('{"email": ', CONCAT('"', @emailAddress, 
     '",'),'"validators": ', '["SyntaxValidator",', '"MXValidator",', 
     '"ListDetectiveValidator"',']}')

     set @apitoken = Lookup('AccessToken','AccessToken','id',1)
     set @accessToken = CONCAT('BEARER ', @apitoken)

     set @url = "https://www.exacttargetapis.com/address/v1/validateEmail"

     set @httppost = HTTPPost2(@url, "application/json", 
     @body, true, @callstatus, @response, 'Authorization', @accessToken)

     endif

     endif

 ]%%

SSJS:

<script language="javascript" runat="server">
} 
</script></span>

AMPSCRIPT:

%%[ if RequestParameter("Submit") == "Submit" AND @httppost == 200 then ]%%

EmailAddress: <span style="color:darkblue;">%%=v(@emailAddress)=%%</span><br>
body: <span style="color:darkblue;">%%=v(@body)=%%</span><br>
callstatus: <span style="color:darkblue;">%%=v(@callstatus)=%%</span><br>
httppost: <span style="color:darkblue;">%%=v(@httppost)=%%</span><br><br>

%%{={{ }}=}%%

{{.datasource JSONVar type=variable}}
  {{.data}}
    { "target" : "@callstatus" }
  {{/data}}
  {{#if valid == "true"}}
    email: <span style="color:green">{{email}}</span><br>
    valid: <span style="color:green">{{valid}}</span><br>
  {{.else}}
    email: <span style="color:red">{{email}}</span><br>
    valid: <span style="color:red">{{valid}}</span><br>
    failedValidation: <span style="color:red">{{failedValidation}}</span>
  {{/if}}
{{/datasource}}

%%[ endif ]%%

Best Answer

There are few mistakes in your logic

You have 3 HTTPPost2 requests in your code that validate your email. 3rd is not needed. Thats the one after the /* part B - Validate EmailAddress */ comment, the one above it does exactly the same (you defined @url, and @body before making 1st HTTPPost2). Cutting this whole part fixes your code:

/* part B - Validate EmailAddress */
set @body = CONCAT('{"email": ', CONCAT('"', @emailAddress, '",'),'"validators": ', '["SyntaxValidator",', '"MXValidator",', '"ListDetectiveValidator"',']}')
set @apitoken = Lookup('AccessToken','AccessToken','id',1)
set @accessToken = CONCAT('BEARER ', @apitoken)
set @url = "https://www.exacttargetapis.com/address/v1/validateEmail"
set @httppost = HTTPPost2(@url, "application/json", @body, true, @callstatus, @response, 'Authorization', @accessToken)

This request was also causing your error, as token for it was empty/null. This was because you tried to get it from DE, but your UpsertData() function was unable to store it before you tried to retrieve it with Lookup('AccessToken','AccessToken','id',1). This led to it being unathorized. Since it was outside of your try{} it caused LP to error out.

Also - there's easier way to check for 401 unathorized request with just ampscript. For all your HTTPPost2 functions you can set 4th parameter to false instead of true. This will not produce an error so try{} catch{}will not be needed. Intead it will return status code for your request. You can use that status code in ampscript IF/ELSE statement to check if it's authorized or not.

set @httppost = HTTPPost2(@url, "application/json", @body, false, @callstatus, @response, 'Authorization', @accessToken)
if @httppost == 401  then
    /* refresh and store the token*/
endif
Related Topic