1
1
using System ;
2
+ using System . IO ;
2
3
using System . Net ;
3
4
using System . Net . Http ;
5
+ using System . Text . Encodings . Web ;
4
6
using System . Threading . Tasks ;
5
- using Microsoft . AspNetCore . Mvc . Rendering ;
6
7
using Microsoft . AspNetCore . Html ;
8
+ using Microsoft . AspNetCore . Mvc . Rendering ;
7
9
using Microsoft . Extensions . Logging ;
8
10
using Newtonsoft . Json ;
9
11
10
12
namespace Microsoft . AspNetCore . AngularServices
11
13
{
12
14
public static class PrimeCacheHelper
13
15
{
14
- public static async Task < HtmlString > PrimeCache ( this IHtmlHelper html , string url )
16
+ public static async Task < IHtmlContent > PrimeCache ( this IHtmlHelper html , string url )
15
17
{
16
18
// TODO: Consider deduplicating the PrimeCache calls (that is, if there are multiple requests to precache
17
19
// the same URL, only return nonempty for one of them). This will make it easier to auto-prime-cache any
@@ -33,26 +35,55 @@ public static async Task<HtmlString> PrimeCache(this IHtmlHelper html, string ur
33
35
request . Path . ToUriComponent ( ) ,
34
36
request . QueryString . ToUriComponent ( ) ) ;
35
37
var fullUri = new Uri ( new Uri ( baseUriString ) , url ) ;
36
- var response = await new HttpClient ( ) . GetAsync ( fullUri . ToString ( ) ) ;
37
- var responseBody = await response . Content . ReadAsStringAsync ( ) ;
38
- return new HtmlString ( FormatAsScript ( url , response . StatusCode , responseBody ) ) ;
38
+ var response = await new HttpClient ( ) . GetAsync ( fullUri . ToString ( ) ) . ConfigureAwait ( false ) ;
39
+ var responseBody = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
40
+ return new PrimeCacheScript ( url , response . StatusCode , responseBody ) ;
39
41
}
40
42
catch ( Exception ex )
41
43
{
42
44
var logger = ( ILogger ) html . ViewContext . HttpContext . RequestServices . GetService ( typeof ( ILogger ) ) ;
43
45
logger ? . LogWarning ( "Error priming cache for URL: " + url , ex ) ;
44
- return new HtmlString ( string . Empty ) ;
46
+ return HtmlString . Empty ;
45
47
}
46
48
}
47
49
48
- private static string FormatAsScript ( string url , HttpStatusCode responseStatusCode , string responseBody )
50
+ private sealed class PrimeCacheScript : IHtmlContent
49
51
{
50
- var preCachedUrl = JsonConvert . SerializeObject ( url ) ;
51
- var preCachedJson = JsonConvert . SerializeObject ( new { statusCode = responseStatusCode , body = responseBody } ) ;
52
- return "<script>"
53
- + "window.__preCachedResponses = window.__preCachedResponses || {};"
54
- + $ "window.__preCachedResponses[{ preCachedUrl } ] = { preCachedJson } ;"
55
- + "</script>" ;
52
+ private readonly string _url ;
53
+ private readonly HttpStatusCode _responseStatusCode ;
54
+ private readonly string _responseBody ;
55
+
56
+ public PrimeCacheScript ( string url , HttpStatusCode responseStatusCode , string responseBody )
57
+ {
58
+ _url = url ;
59
+ _responseStatusCode = responseStatusCode ;
60
+ _responseBody = responseBody ;
61
+ }
62
+
63
+ // These properties exist to be serialized as JSON without having to allocate an anonymous object.
64
+ public HttpStatusCode statusCode => _responseStatusCode ;
65
+ public string body => _responseBody ;
66
+
67
+ public void WriteTo ( TextWriter writer , HtmlEncoder encoder )
68
+ {
69
+ if ( writer == null )
70
+ {
71
+ throw new ArgumentNullException ( nameof ( writer ) ) ;
72
+ }
73
+
74
+ if ( encoder == null )
75
+ {
76
+ throw new ArgumentNullException ( nameof ( encoder ) ) ;
77
+ }
78
+
79
+ var serializer = new JsonSerializer ( ) ;
80
+ var jsonWriter = new JsonTextWriter ( writer ) ;
81
+ jsonWriter . WriteRaw ( "<script>window.__preCachedResponses=window.__preCachedResponses||{},window.__preCachedResponses[" ) ;
82
+ serializer . Serialize ( jsonWriter , _url ) ;
83
+ jsonWriter . WriteRaw ( "]=" ) ;
84
+ serializer . Serialize ( jsonWriter , this ) ;
85
+ jsonWriter . WriteRaw ( ";</script>" ) ;
86
+ }
56
87
}
57
88
}
58
89
}
0 commit comments