1

I am using ServiceStack with SharpPages to render dynamic content. For "reasons", I need to set the CORS headers Access-Control-Allow-Origin and Access-Control-Allow-Credentials, supporting multiple subdomains.

My SharpPages feature is enabled with :

var pagesFeature = new SharpPagesFeature()
        {
            ScriptMethods = { new UrlScriptMethods(), new DbScriptsAsync() },
        };
        pagesFeature.Args[ServiceStack.Script.ScriptConstants.DefaultDateFormat] = "MM/dd/yyyy hh:mm";
        pagesFeature.Args[ServiceStack.Script.ScriptConstants.DefaultDateTimeFormat] = "MM/dd/yyyy hh:mm";
        Plugins.Add(pagesFeature);

I'm hosting on IIS, so I could use web.config like below, but I can only specify one domain this way. If I specify multiple, XMLHttpRequest calls complain there are multiple domains set for that header.

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="https://subdomain.domain.com" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Likewise, I could have used the ServiceStack HostConfig property GlobalResponseHeaders, but same deal.

I've even tried ServiceStack PreRequestFilters, but those aren't called unless a service method is called. Here is my filter:

this.PreRequestFilters.Add((httpReq, httpResp) =>
        {
            var origin = httpReq.Headers.Get(HttpHeaders.Origin);
            if (!string.IsNullOrWhiteSpace(origin))
            {
                httpResp.AddHeader(HttpHeaders.AllowOrigin, origin);
                httpResp.AddHeader(HttpHeaders.AllowCredentials, "true");
            }
        });

Finally, StaticFileHandler.ResponseFilter won't work, since I'm using a view engine and not static files.

So, how can I add custom response headers to View Pages (SharpPages in particular, possibly Razor pages as well) in ServiceStack?

The raw request is below. Interesting that I'm requesting https://computer.domain but FireFox translates that to localhost. Regardless, the favicon.ico request DOES get trapped by the filter. The request below DOES NOT.

GET /forms/newsletter HTTP/1.1
Host: localhost:44308
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: ss-pid=wCR4INmjLXpBnbsBoe2n
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

The raw response is :

HTTP/2.0 200 OK
cache-control: private
content-type: text/html
content-encoding: gzip
vary: Accept-Encoding
server: Microsoft-IIS/10.0
x-aspnet-version: 4.0.30319
x-sourcefiles: =?UTF-8?B?QzpcVXNlcnNcamtsZW1tYWNrXFNvdXJjZVxSZXBvc1xPQlJDX0JNU1xCTVMuV2ViLkJvdHRsZURyb3BDZW50ZXJzXEJNUy5XZWIuQm90dGxlRHJvcENlbnRlcnNcZm9ybXNcbmV3c2xldHRlcg==?=
x-powered-by: ASP.NET
access-control-allow-origin: *
date: Tue, 11 Jun 2019 16:28:34 GMT
content-length: 862
X-Firefox-Spdy: h2
jklemmack
  • 3,518
  • 3
  • 30
  • 56
  • `PreRequestFilters` should be called for all ServiceStack requests, which Sharp Pages request doesn't call the `PreRequestFilters`? – mythz Jun 11 '19 at 15:28
  • @mythz I've added my SharpPages feature call & the pre-request filter to the question above. Local site is hosted via internal network name (`http://computer.domain`). I created a 2nd local site hosted on 127.0.0.1, and am loading the SharpPage via `$.ajax({ crossDomain: true, ...})` on page load. The `PreRequestFilter` is not hit at all from the, XMLHttpRequest, but the SharpPage is being returned. Using Chrome & FireFox with caching disabled via DevTools. – jklemmack Jun 11 '19 at 16:03
  • Is this a content page or a view page (I.e in response to a Service)? I’ve tested that content pages do call the filter. Are you testing whether the filter gets called in a debugger? – mythz Jun 11 '19 at 16:10
  • Content page - no backing Service. I have the debugger set on the filter and it's not tripping. Even added logging in case of wonky VS debugger and still nada. It *does* trip when I navigate directly from the browser, but DOES NOT when I load via $.ajax. I added in a cache breaker (?t= [date time ticks]) - no different. Why would SS treat an ajax call differently? – jklemmack Jun 11 '19 at 16:17
  • Can you post the raw HTTP Request/Response Headers? Also include {{now}} in your page so you can verify a cached response isn’t being returned. – mythz Jun 11 '19 at 16:20
  • Posted above. As I mentioned, the `favicon.ico` gets stopped. The `forms/newsletter` does not. – jklemmack Jun 11 '19 at 16:32
  • There’s no indication that request is being handled by ServiceStack? Is this a Sharp Page that’s being rendered or a static .html file? – mythz Jun 11 '19 at 16:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/194774/discussion-between-jklemmack-and-mythz). – jklemmack Jun 11 '19 at 16:49

2 Answers2

1

The PreRequestFilters should now be fired for all Razor and Sharp Pages requests from the latest v5.5.1+ that's now available on MyGet.

mythz
  • 141,670
  • 29
  • 246
  • 390
0

In the meantime, I've created a portmanteau. For a particular sub-folder, I'm using web.config to allow all remote origins. For Service-based calls, I'm using a hand-rolled version of CorsFeature (a small amount of custom logic won't work).

<location path="views/subfolder">  <-- applies the ACAO header for specific view pages
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Access-Control-Allow-Origin" value="*" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location> 
jklemmack
  • 3,518
  • 3
  • 30
  • 56