November Happy Hour will be moved to Thursday December 5th.

Forms in iframes... or not?

Vote:
 

Hi All,

Hope you are staying safe.

I'll provide some background to what I'm trying to do, just incase I'm focusing on solving the wrong problem!

Background:

We recently implemented a caching strategy with the help of EPiServer's own Johan Antila. It's a full page cache based on David Knipe's caching with Visitor Groups, in which Johan modified the solution to also work with A/B testing. And it's awesome, we saw our TTFB drop from around 1 - 2s down to 200 - 500ms when the cached version of a page is requested. We are super happy, but then we discovered we had overlooked Forms! (how could we!?!) Antiforgery tokens and caching don't mix :/

I quickly modified the implementation to not cache a page if there was a form present, but a significant portion of our pages have a form on and it's ruining the good vibes. On top of that, we know that this is only a temporary solution until we can work out how to handle forms. Maybe some kind of donut hole caching mixed in with what we have? Or forms in an iframe?

My journey so far:

Given how close we are with the caching implementation, I've shied away from any donuts and I guess I fear that I'll end up going down that road eventually. But not before I've exhausted the iframe possibility.

And I feel like I've got quite far with an iframe solution. I've created an iframe block type. I've created a new form container page type to only hold a single form, and restricted the iframeblock to only reference a single one of these new formcontainerpages. The formcontainpage has it's own layout so I'm able to strip out any calls to the main site's javascript files and redundant meta tags etc and the result is a page containing just a form being loaded inside an iframe. The parent page is cached nicely and the iframed form container page is not, the antiforgerytoken stuff is all happy and forms are basically working.

Gotchas:

We like a thank you page! When users complete a form they are generally taken to a thank you page and we use that hit for conversion tracking etc. But no one wants to see the thank you page loaded just inside the iframe... I can of course write some js to use the MutationObserver api to detect when the iframe changes url and use that to redirect the parent page of the iframe, but this of course results in two hits to the thank you page and that just won't do.

For seemingly obvious security reasons, you can't use a window's onbeforeload event to grab the intended destination page and stop the iframe from requesting the thank you page before redirecting the parent page.

I thought perhaps I could hook into FormSubmissionFinalized event from Forms and somehow see the final destination url there, but I can't even see that firing even though the submission data in the CMS shows as finalized. But given the data submission stuff is all handled on the server, that was probablly a silly idea in the first place.

So I began wondering about custom actors. Could I write a custom actor which would optionally overwrite the redirecturl functionality of the forms and somehow hook that into the iframe to post a message to the parent and force the parent page to redirect instead?

My question(s):

So finally to my questions. Is that possible? How would that even work?

I've been looking at the decompiled code from Forms and from Forms.Demo and I can't quite see yet how that is implemented. I'm guessing that there'll be some code doing something like RedirectToAction(RedirectUrl? + FormSessionData) and I'd need an actor to overwrite that method. But then what? a custom action method which passes the data to a script I can then use in the iframe to assign window.top.location.href ?? I'm not really sure.

Am I barking up the completely wrong tree? Is the approach I am trying to take just not possible?

If anyone can help, I would be so so so grateful!

All the best,
Alex

#225967
Jul 31, 2020 13:20
Vote:
 

... it's setting target="_parent" to the <form> isn't it...? no it's not. not setting it to _top either. lol once again, I thought I had it.

I also found where RedirectToUrl is in forms. It's buried deep in EPiServer.Forms.Core.Internal.DataSubmissionService redirectUrl used as argumenment for this.BuildReturnResultsForSubmitAction() so without forking EPiServer.Forms and rewriting the internals I don't see how I'm going to do that ha ha ha even if I could!!!

#225969
Edited, Jul 31, 2020 14:49
Vote:
 

Hello Alex

First of all thanks for participating in the form and sharing your solution - great to hear the solution works for you (forms aside...).  

As for solving the forms challenge you could make the "thank you" page thats redirected to in the iframe simply be an empty/dumb page that does nothing but break out of the frame and redirect to the actual thank you page using some JS? Something site site.com/thankyou.html?redirect?/actual-thank-you-page/ where the JS reads the redirect parameter, breaks out of the frame and redirects to /actual-thank-you-page/

This could solve your challenge and avoid double tracking?

David

#226449
Edited, Aug 13, 2020 8:17
Alex Brown - Aug 17, 2020 7:18
Hey David,

Thank you for writing that caching POC all those years ago!

A simple additional page which does nothing but break out of an iframe could work! I guess I would have to set the form's thank you page field to point to this additional page and it would need at least one property holding the final thank you page url which I can then get the JS to redirect the container page too.

If I focus on keeping it as lightweight as possible this could be pretty cool. No controller, no layout, just an inline script in a view...

Once again, appreciate your help David.

Alex
David Knipe - Aug 17, 2020 9:56
Hey Alex

Let me know how it goes - I'm always curious to hear about implementations!

David
Alex Brown - Aug 19, 2020 14:27
Hi David,

It works like a charm!

Created a new RedirectPage type with a single property to store a page reference to the final destination page. hooked it up with a controller and a little view model and put the quick logic in there to get the url of the final destination page and append any query strings in case we ever do some personalisation.
No layout for the view, just a quick script to set window.top.location to the url with query strings and boom! iframe busted :)

Only checked stats locally so far, but it seems to only add ~700B to the data consumption and added an extra 400ms to the user wait from clicking submit to landing on the thank you page.

Thanks again for your help!

Alex
David Knipe - Aug 19, 2020 15:35
Hey Alex

Really glad it seems to work!

David
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.