Skip to content

Commit 66465df

Browse files
committed
fix blobserviceclient construction
1 parent 5f3fba2 commit 66465df

1 file changed

Lines changed: 149 additions & 136 deletions

File tree

Web/Program.cs

Lines changed: 149 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
// Add route configuration to enforce lowercase URLs for better SEO
2020
builder.Services.Configure<RouteOptions>(options =>
2121
{
22-
options.LowercaseUrls = true;
23-
options.LowercaseQueryStrings = true;
24-
options.AppendTrailingSlash = false;
22+
options.LowercaseUrls = true;
23+
options.LowercaseQueryStrings = true;
24+
options.AppendTrailingSlash = false;
2525
});
2626

2727
builder.AddAzureTableClient("tables");
@@ -34,45 +34,45 @@
3434
// Configure Redis key prefixes for better organization
3535
builder.Services.PostConfigure<Microsoft.Extensions.Caching.StackExchangeRedis.RedisCacheOptions>(options =>
3636
{
37-
options.InstanceName = "CopilotThatJawn:";
37+
options.InstanceName = "CopilotThatJawn:";
3838
});
3939

4040
// Add WebOptimizer services
4141
builder.Services.AddWebOptimizer(pipeline =>
4242
{
43-
if (!builder.Environment.IsDevelopment())
44-
{
45-
// Bundle and minify CSS files in production only
46-
pipeline.AddCssBundle("/css/bundle.min.css",
47-
"css/site.css",
48-
"css/layout.css");
49-
50-
// Bundle and minify JavaScript files in production only
51-
pipeline.AddJavaScriptBundle("/js/bundle.min.js",
52-
"js/site.js",
53-
"js/analytics.js",
54-
"js/theme-switcher.js");
55-
56-
// Enable minification for all CSS files
57-
pipeline.MinifyCssFiles();
58-
59-
// Enable minification for all JavaScript files
60-
pipeline.MinifyJsFiles();
61-
}
62-
else
63-
{
64-
// In development, still enable basic minification for testing
65-
pipeline.MinifyCssFiles("css/*.css");
66-
pipeline.MinifyJsFiles("js/*.js");
67-
}
43+
if (!builder.Environment.IsDevelopment())
44+
{
45+
// Bundle and minify CSS files in production only
46+
pipeline.AddCssBundle("/css/bundle.min.css",
47+
"css/site.css",
48+
"css/layout.css");
49+
50+
// Bundle and minify JavaScript files in production only
51+
pipeline.AddJavaScriptBundle("/js/bundle.min.js",
52+
"js/site.js",
53+
"js/analytics.js",
54+
"js/theme-switcher.js");
55+
56+
// Enable minification for all CSS files
57+
pipeline.MinifyCssFiles();
58+
59+
// Enable minification for all JavaScript files
60+
pipeline.MinifyJsFiles();
61+
}
62+
else
63+
{
64+
// In development, still enable basic minification for testing
65+
pipeline.MinifyCssFiles("css/*.css");
66+
pipeline.MinifyJsFiles("js/*.js");
67+
}
6868
});
6969

7070
// Add services to the container.
7171
builder.Services.AddRazorPages()
72-
.AddRazorPagesOptions(options =>
73-
{
74-
options.RootDirectory = "/Pages";
75-
});
72+
.AddRazorPagesOptions(options =>
73+
{
74+
options.RootDirectory = "/Pages";
75+
});
7676

7777
builder.Services.AddControllers(); // Add controller support for API endpoints
7878
builder.Services.AddMvc().AddViewComponentsAsServices(); // Register view components
@@ -84,64 +84,77 @@
8484
// Configure output cache policies (Redis is already configured above via AddRedisOutputCache)
8585
builder.Services.Configure<Microsoft.AspNetCore.OutputCaching.OutputCacheOptions>(options =>
8686
{
87-
// Default site-wide caching policy - extended to 6 hours for better performance
88-
options.AddBasePolicy(builder =>
89-
builder.Cache()
90-
.SetVaryByHost(true)
91-
.SetVaryByQuery("*")
92-
.SetVaryByHeader("Accept-Language") // Vary by language
93-
.Expire(TimeSpan.FromHours(6)) // Cache for 6 hours by default
94-
.Tag("outputcache", "site")); // Add tags for better organization
95-
96-
// Special policy for static content pages - extended to 3 days
97-
options.AddPolicy("StaticContent", builder =>
98-
builder.Cache()
99-
.SetVaryByHost(true)
100-
.Expire(TimeSpan.FromDays(3)) // Cache static content for 3 days
101-
.Tag("outputcache", "static")); // Add tags for better organization
102-
103-
// Special policy for tips and content pages - extended to 3 days since they're static
104-
options.AddPolicy("TipsContent", builder =>
105-
builder.Cache()
106-
.SetVaryByHost(true)
107-
.SetVaryByRouteValue("slug") // Vary by tip slug
108-
.Expire(TimeSpan.FromDays(3)) // Cache tips for 3 days
109-
.Tag("outputcache", "tips", "content")); // Add tags for better organization
110-
111-
// Policy for frequently updated content - extended to 6 hours minimum
112-
options.AddPolicy("DynamicContent", builder =>
113-
builder.Cache()
114-
.SetVaryByHost(true)
115-
.SetVaryByQuery("*")
116-
.Expire(TimeSpan.FromHours(6)) // Cache dynamic content for 6 hours
117-
.Tag("outputcache", "dynamic")); // Add tags for better organization
87+
// Default site-wide caching policy - extended to 6 hours for better performance
88+
options.AddBasePolicy(builder =>
89+
builder.Cache()
90+
.SetVaryByHost(true)
91+
.SetVaryByQuery("*")
92+
.SetVaryByHeader("Accept-Language") // Vary by language
93+
.Expire(TimeSpan.FromHours(6)) // Cache for 6 hours by default
94+
.Tag("outputcache", "site")); // Add tags for better organization
95+
96+
// Special policy for static content pages - extended to 3 days
97+
options.AddPolicy("StaticContent", builder =>
98+
builder.Cache()
99+
.SetVaryByHost(true)
100+
.Expire(TimeSpan.FromDays(3)) // Cache static content for 3 days
101+
.Tag("outputcache", "static")); // Add tags for better organization
102+
103+
// Special policy for tips and content pages - extended to 3 days since they're static
104+
options.AddPolicy("TipsContent", builder =>
105+
builder.Cache()
106+
.SetVaryByHost(true)
107+
.SetVaryByRouteValue("slug") // Vary by tip slug
108+
.Expire(TimeSpan.FromDays(3)) // Cache tips for 3 days
109+
.Tag("outputcache", "tips", "content")); // Add tags for better organization
110+
111+
// Policy for frequently updated content - extended to 6 hours minimum
112+
options.AddPolicy("DynamicContent", builder =>
113+
builder.Cache()
114+
.SetVaryByHost(true)
115+
.SetVaryByQuery("*")
116+
.Expire(TimeSpan.FromHours(6)) // Cache dynamic content for 6 hours
117+
.Tag("outputcache", "dynamic")); // Add tags for better organization
118118
});
119119

120120
// Add response compression
121121
builder.Services.AddResponseCompression(options =>
122122
{
123-
options.EnableForHttps = true;
124-
options.Providers.Add<BrotliCompressionProvider>();
125-
options.Providers.Add<GzipCompressionProvider>();
126-
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
127-
new[] { "image/svg+xml", "application/javascript", "text/css" });
123+
options.EnableForHttps = true;
124+
options.Providers.Add<BrotliCompressionProvider>();
125+
options.Providers.Add<GzipCompressionProvider>();
126+
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
127+
new[] { "image/svg+xml", "application/javascript", "text/css" });
128128
});
129129

130130
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
131131
{
132-
options.Level = CompressionLevel.Optimal;
132+
options.Level = CompressionLevel.Optimal;
133133
});
134134

135135
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
136136
{
137-
options.Level = CompressionLevel.Optimal;
137+
options.Level = CompressionLevel.Optimal;
138138
});
139139

140140
// Add Azure Blob Storage
141-
builder.Services.AddSingleton(x =>
141+
builder.Services.AddSingleton(x =>
142142
{
143-
var connectionString = builder.Configuration.GetConnectionString("blobs");
144-
return new BlobServiceClient(connectionString);
143+
var connectionString = builder.Configuration.GetConnectionString("blobs");
144+
if (Uri.IsWellFormedUriString(connectionString, UriKind.Absolute))
145+
{
146+
// If the connection string is a URI, use it directly
147+
return new BlobServiceClient(new Uri(connectionString));
148+
}
149+
else
150+
{
151+
// Otherwise, treat it as a standard connection string
152+
if (string.IsNullOrWhiteSpace(connectionString))
153+
{
154+
throw new ArgumentException("Invalid Azure Blob Storage connection string.", nameof(connectionString));
155+
}
156+
return new BlobServiceClient(connectionString);
157+
}
145158
});
146159

147160
// Add Content Service with image handling
@@ -152,7 +165,7 @@
152165
var options = new RewriteOptions()
153166
.AddRedirectToNonWwwPermanent()
154167
.AddRedirect("^tips/tag$", "tips", 301); // Redirect /tips/tag to /tips with 301 (permanent) redirect
155-
//.AddRedirectToHttpsPermanent();
168+
//.AddRedirectToHttpsPermanent();
156169
app.UseRewriter(options);
157170

158171
// Configure the HTTP request pipeline.
@@ -179,71 +192,71 @@
179192

180193
if (!app.Environment.IsDevelopment())
181194
{
182-
app.UseWebOptimizer(); // Use WebOptimizer in production
183-
184-
// Add middleware to handle cache headers for WebOptimizer files
185-
app.Use(async (context, next) =>
186-
{
187-
if (context.Request.Path.StartsWithSegments("/css/bundle.min.css") ||
188-
context.Request.Path.StartsWithSegments("/js/bundle.min.js"))
189-
{
190-
// Set headers to ensure proper cache behavior for bundled files
191-
context.Response.OnStarting(() =>
192-
{
193-
context.Response.Headers.CacheControl = "public,max-age=31536000,immutable";
194-
context.Response.Headers.Vary = "Accept-Encoding";
195-
return Task.CompletedTask;
196-
});
197-
}
198-
await next();
199-
});
195+
app.UseWebOptimizer(); // Use WebOptimizer in production
196+
197+
// Add middleware to handle cache headers for WebOptimizer files
198+
app.Use(async (context, next) =>
199+
{
200+
if (context.Request.Path.StartsWithSegments("/css/bundle.min.css") ||
201+
context.Request.Path.StartsWithSegments("/js/bundle.min.js"))
202+
{
203+
// Set headers to ensure proper cache behavior for bundled files
204+
context.Response.OnStarting(() =>
205+
{
206+
context.Response.Headers.CacheControl = "public,max-age=31536000,immutable";
207+
context.Response.Headers.Vary = "Accept-Encoding";
208+
return Task.CompletedTask;
209+
});
210+
}
211+
await next();
212+
});
200213
}
201214

202215
app.UseStaticFiles(new StaticFileOptions
203216
{
204-
OnPrepareResponse = ctx =>
205-
{
206-
if (!app.Environment.IsDevelopment())
207-
{
208-
var path = ctx.Context.Request.Path.Value?.ToLowerInvariant();
209-
210-
// Different caching strategies based on file type and path
211-
if (path != null)
212-
{
213-
// WebOptimizer bundles and files with version query strings - cache aggressively
214-
if (path.Contains("bundle.min.") || ctx.Context.Request.Query.ContainsKey("v"))
215-
{
216-
ctx.Context.Response.Headers.CacheControl = "public,max-age=31536000,immutable"; // 1 year
217-
}
218-
// Regular CSS/JS files - shorter cache with validation
219-
else if (path.EndsWith(".css") || path.EndsWith(".js"))
220-
{
221-
ctx.Context.Response.Headers.CacheControl = "public,max-age=3600,must-revalidate"; // 1 hour
222-
}
223-
// Images and fonts - medium cache
224-
else if (path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".jpeg") ||
225-
path.EndsWith(".gif") || path.EndsWith(".svg") || path.EndsWith(".webp") ||
226-
path.EndsWith(".woff") || path.EndsWith(".woff2") || path.EndsWith(".ttf"))
227-
{
228-
ctx.Context.Response.Headers.CacheControl = "public,max-age=2592000"; // 30 days
229-
}
230-
// Other static files - short cache
231-
else
232-
{
233-
ctx.Context.Response.Headers.CacheControl = "public,max-age=3600"; // 1 hour
234-
}
235-
}
236-
237-
ctx.Context.Response.Headers.Vary = "Accept-Encoding";
238-
}
239-
else
240-
{
241-
// Disable caching in development
242-
ctx.Context.Response.Headers.CacheControl = "no-cache, no-store";
243-
ctx.Context.Response.Headers.Pragma = "no-cache";
244-
ctx.Context.Response.Headers.Expires = "-1";
245-
}
246-
}
217+
OnPrepareResponse = ctx =>
218+
{
219+
if (!app.Environment.IsDevelopment())
220+
{
221+
var path = ctx.Context.Request.Path.Value?.ToLowerInvariant();
222+
223+
// Different caching strategies based on file type and path
224+
if (path != null)
225+
{
226+
// WebOptimizer bundles and files with version query strings - cache aggressively
227+
if (path.Contains("bundle.min.") || ctx.Context.Request.Query.ContainsKey("v"))
228+
{
229+
ctx.Context.Response.Headers.CacheControl = "public,max-age=31536000,immutable"; // 1 year
230+
}
231+
// Regular CSS/JS files - shorter cache with validation
232+
else if (path.EndsWith(".css") || path.EndsWith(".js"))
233+
{
234+
ctx.Context.Response.Headers.CacheControl = "public,max-age=3600,must-revalidate"; // 1 hour
235+
}
236+
// Images and fonts - medium cache
237+
else if (path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".jpeg") ||
238+
path.EndsWith(".gif") || path.EndsWith(".svg") || path.EndsWith(".webp") ||
239+
path.EndsWith(".woff") || path.EndsWith(".woff2") || path.EndsWith(".ttf"))
240+
{
241+
ctx.Context.Response.Headers.CacheControl = "public,max-age=2592000"; // 30 days
242+
}
243+
// Other static files - short cache
244+
else
245+
{
246+
ctx.Context.Response.Headers.CacheControl = "public,max-age=3600"; // 1 hour
247+
}
248+
}
249+
250+
ctx.Context.Response.Headers.Vary = "Accept-Encoding";
251+
}
252+
else
253+
{
254+
// Disable caching in development
255+
ctx.Context.Response.Headers.CacheControl = "no-cache, no-store";
256+
ctx.Context.Response.Headers.Pragma = "no-cache";
257+
ctx.Context.Response.Headers.Expires = "-1";
258+
}
259+
}
247260
});
248261

249262
app.UseRouting();

0 commit comments

Comments
 (0)