When using Optimizely Search & Navigation (previously Episerver Find) you will automatically get a reference to a client side Javascript injected into your markup.
<script type="text/javascript" src="https://dl.episerver.net/13.4.4.1/epi-util/find.js"></script>
Occationally, you may want to proxy it via your Optimizely website’s backend, caching it or just make it appear as if it comes from your own domain. It may also be a way of avoiding false positives in regards to script tags not using Subresource Integrity (SRI) hashes where you control the script location yourself.
Edit: see article Finding the latest Optimizely Search & Navigation’s client Javascript URL for a simple way of getting the URL to Optimizely’s latest script.
public class ExternalResourceProxyController : Controller
{
private const string _jsContentType = "application/javascript";
// Constant value in example to reduce noise.
private const string _scriptUrl = "https://dl.episerver.net/13.4.4.1/epi-util/find.js";
private readonly ExternalResourceService _externalResourceService;
public ExternalResourceProxyController (ExternalResourceService externalResourceService)
{
_externalResourceService = externalResourceService ?? throw new ArgumentNullException (nameof(externalResourceService));
}
[HttpGet, Route("ClientResources/static/js/find.js")]
[OutputCache(VaryByHeader = "Host", Duration = 10 * 60, Location = OutputCacheLocation.Any)]
public async Task<ContentResult> EpiserverFindScript()
{
string content = await _externalResourceService .GetContentAsync(_scriptUrl) .ConfigureAwait(false);
return new ContentResult
{
Content = content,
ContentType = _jsContentType,
};
}
The ExternalResourceService is just a simple disposable class making the call to retireve the client side script, then returning it as a string. You may want to include proper error handling here.
public class ExternalResourceService : IDisposable
{
/// <remarks>
/// Register as a singleton.
/// </remarks>
internal ExternalResourceService() { }
private HttpClient _httpClient = new HttpClient();
public async Task<string> GetContentAsync(Uri url)
{
try
{
using (HttpResponseMessage response = await _httpClient.GetAsync(url).ConfigureAwait(false))
{
_ = response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
catch (Exception ex)
{
// ...
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing && _httpClient != null)
{
_httpClient.Dispose();
_httpClient = null;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
To make Optimizely Search & Navigation use your new script path, update the modules.config file with a new clientResource
called epi.find.trackingScript
.
<module>
<clientResources>
<add name="epi.find.trackingScript" path="static/js/find.js" resourceType="Script"/>
Note that the web.config’s system.webServer/caching/profiles/add
needs to be updated with varyByHeaders="Host"
for extensions used in this proxy (like for instance .js
).