1- using System . Diagnostics . CodeAnalysis ;
2- using System . Reflection ;
3- using System . Text ;
1+ using System . Text ;
42using System . Text . Json ;
5- using System . Text . Json . Serialization ;
63using System . Text . RegularExpressions ;
74using Microsoft . AspNetCore . Builder ;
8- using Microsoft . AspNetCore . Hosting ;
95using Microsoft . AspNetCore . Http ;
106using Microsoft . AspNetCore . StaticFiles ;
117using Microsoft . Extensions . FileProviders ;
128using Microsoft . Extensions . Logging ;
139using Microsoft . Extensions . Options ;
10+ using System . Reflection ;
11+ using System . Security . Cryptography ;
1412
15- #if ! NET
13+ #if NET
14+ using System . Diagnostics . CodeAnalysis ;
15+ using Microsoft . AspNetCore . Hosting ;
16+ #else
17+ using System . Text . Json . Serialization ;
1618using IWebHostEnvironment = Microsoft . AspNetCore . Hosting . IHostingEnvironment ;
1719#endif
1820
@@ -22,6 +24,8 @@ internal sealed partial class SwaggerUIMiddleware
2224{
2325 private const string EmbeddedFileNamespace = "Swashbuckle.AspNetCore.SwaggerUI.node_modules.swagger_ui_dist" ;
2426
27+ private static readonly string SwaggerUIVersion = GetSwaggerUIVersion ( ) ;
28+
2529 private readonly SwaggerUIOptions _options ;
2630 private readonly StaticFileMiddleware _staticFileMiddleware ;
2731 private readonly JsonSerializerOptions _jsonSerializerOptions ;
@@ -106,12 +110,47 @@ private static StaticFileMiddleware CreateStaticFileMiddleware(
106110 var staticFileOptions = new StaticFileOptions
107111 {
108112 RequestPath = string . IsNullOrEmpty ( options . RoutePrefix ) ? string . Empty : $ "/{ options . RoutePrefix } ",
109- FileProvider = new EmbeddedFileProvider ( typeof ( SwaggerUIMiddleware ) . GetTypeInfo ( ) . Assembly , EmbeddedFileNamespace ) ,
113+ FileProvider = new EmbeddedFileProvider ( typeof ( SwaggerUIMiddleware ) . Assembly , EmbeddedFileNamespace ) ,
114+ OnPrepareResponse = ( context ) => SetCacheHeaders ( context . Context . Response , options ) ,
110115 } ;
111116
112117 return new StaticFileMiddleware ( next , hostingEnv , Options . Create ( staticFileOptions ) , loggerFactory ) ;
113118 }
114119
120+ private static string GetSwaggerUIVersion ( )
121+ {
122+ return typeof ( SwaggerUIMiddleware ) . Assembly
123+ . GetCustomAttributes < AssemblyMetadataAttribute > ( )
124+ . Where ( ( p ) => p . Key is "SwaggerUIVersion" )
125+ . Select ( ( p ) => p . Value )
126+ . DefaultIfEmpty ( string . Empty )
127+ . FirstOrDefault ( ) ;
128+ }
129+
130+ private static void SetCacheHeaders ( HttpResponse response , SwaggerUIOptions options , string etag = null )
131+ {
132+ var headers = response . GetTypedHeaders ( ) ;
133+
134+ if ( options . CacheLifetime is { } maxAge )
135+ {
136+ headers . CacheControl = new ( )
137+ {
138+ MaxAge = maxAge ,
139+ Private = true ,
140+ } ;
141+ }
142+ else
143+ {
144+ headers . CacheControl = new ( )
145+ {
146+ NoCache = true ,
147+ NoStore = true ,
148+ } ;
149+ }
150+
151+ headers . ETag = new ( $ "\" { etag ?? SwaggerUIVersion } \" ", isWeak : true ) ;
152+ }
153+
115154 private static void RespondWithRedirect ( HttpResponse response , string location )
116155 {
117156 response . StatusCode = StatusCodes . Status301MovedPermanently ;
@@ -150,10 +189,29 @@ private async Task RespondWithFile(HttpResponse response, string fileName)
150189 content . Replace ( entry . Key , entry . Value ) ;
151190 }
152191
153- await response . WriteAsync ( content . ToString ( ) , Encoding . UTF8 ) ;
192+ var text = content . ToString ( ) ;
193+ var etag = HashText ( text ) ;
194+
195+ SetCacheHeaders ( response , _options , etag ) ;
196+
197+ await response . WriteAsync ( text , Encoding . UTF8 ) ;
154198 }
155199 }
156200
201+ private static string HashText ( string text )
202+ {
203+ var buffer = Encoding . UTF8 . GetBytes ( text ) ;
204+
205+ #if NET
206+ var hash = SHA1 . HashData ( buffer ) ;
207+ #else
208+ using var sha = SHA1 . Create ( ) ;
209+ var hash = sha . ComputeHash ( buffer ) ;
210+ #endif
211+
212+ return Convert . ToBase64String ( hash ) ;
213+ }
214+
157215#if NET
158216 [ UnconditionalSuppressMessage (
159217 "AOT" ,
0 commit comments