Skip to content

Commit b893b49

Browse files
committed
Add [RouteCache] attribute for per-route cache control
Introduces the [RouteCache] attribute to allow declarative, per-route caching configuration in Blazouter. Updates documentation (ATTRIBUTE_ROUTING.md, CACHING.md) with usage details and examples, adds a sample page demonstrating the attribute, and extends RouteAttributeDiscoveryService to support EnableCache from attributes.
1 parent e7be125 commit b893b49

File tree

7 files changed

+508
-176
lines changed

7 files changed

+508
-176
lines changed

ATTRIBUTE_ROUTING.md

Lines changed: 96 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ Blazouter supports both programmatic route configuration (using `RouteConfig` ob
66

77
Attribute-based routing allows you to decorate your Blazor components with attributes that define their routing configuration. This approach:
88

9-
- **✅ Declarative** - Clear, easy-to-read syntax
10-
- **✅ Flexible** - Can be mixed with programmatic routes as needed
11-
- **✅ More intuitive** - Route configuration is co-located with the component
12-
- **✅ Backward compatible** - Works alongside existing programmatic routing
9+
- **✅ Declarative** - Clear, easy-to-read syntax
10+
- **✅ Flexible** - Can be mixed with programmatic routes as needed
11+
- **✅ More intuitive** - Route configuration is co-located with the component
12+
- **✅ Backward compatible** - Works alongside existing programmatic routing
1313

1414
## Available Attributes
1515

@@ -28,7 +28,8 @@ public class AdminPage : ComponentBase
2828
**Note:** Named `Route` instead of `Route` to avoid conflicts with Blazor's built-in `RouteAttribute`.
2929

3030
**Parameters:**
31-
- `path` (string) - The route path pattern. Supports dynamic parameters with `:` prefix (e.g., `/users/:id`)
31+
32+
- `path` (string) - The route path pattern. Supports dynamic parameters with `:` prefix (e.g., `/users/:id`)
3233

3334
### `[RouteTransition]` - Set Animation
3435

@@ -44,7 +45,8 @@ public class AboutPage : ComponentBase
4445
```
4546

4647
**Parameters:**
47-
- `transition` (RouteTransition enum) - The animation type (Fade, Slide, Scale, etc.)
48+
49+
- `transition` (RouteTransition enum) - The animation type (Fade, Slide, Scale, etc.)
4850

4951
### `[RouteMiddleware]` - Add Route Middleware
5052

@@ -62,16 +64,18 @@ public class AdminPage : ComponentBase
6264
```
6365

6466
**Parameters:**
65-
- `middlewareType` (Type) - The type of the middleware class (must implement `IRouteMiddleware`)
67+
68+
- `middlewareType` (Type) - The type of the middleware class (must implement `IRouteMiddleware`)
6669

6770
**Note:** Middleware execute in the order they are declared, before guards. Middleware can execute code before and after navigation, share data with components, and abort or redirect navigation.
6871

6972
**Common Use Cases:**
70-
- Logging and analytics tracking
71-
- Performance monitoring
72-
- Data preloading
73-
- Feature flags
74-
- Session management
73+
74+
- Logging and analytics tracking
75+
- Performance monitoring
76+
- Data preloading
77+
- Feature flags
78+
- Session management
7579

7680
### `[RouteGuard]` - Add Access Control
7781

@@ -88,7 +92,8 @@ public class AdminPage : ComponentBase
8892
```
8993

9094
**Parameters:**
91-
- `guardType` (Type) - The type of the guard class (must implement `IRouteGuard`)
95+
96+
- `guardType` (Type) - The type of the guard class (must implement `IRouteGuard`)
9297

9398
**Note:** Guards execute in the order they are declared, after middleware.
9499

@@ -106,7 +111,8 @@ public class AdminPage : ComponentBase
106111
```
107112

108113
**Parameters:**
109-
- `layoutType` (Type?) - The layout component type (must inherit from `LayoutComponentBase`), or `null` for no layout
114+
115+
- `layoutType` (Type?) - The layout component type (must inherit from `LayoutComponentBase`), or `null` for no layout
110116

111117
### `[RouteTitle]` - Set Page Title
112118

@@ -122,7 +128,8 @@ public class AboutPage : ComponentBase
122128
```
123129

124130
**Parameters:**
125-
- `title` (string) - The route title
131+
132+
- `title` (string) - The route title
126133

127134
### `[RouteData]` - Add Custom Data
128135

@@ -137,14 +144,15 @@ public class AdminPage : ComponentBase
137144
// Only define parameters you need - others are automatically filtered
138145
[Parameter]
139146
public string? Section { get; set; }
140-
147+
141148
// RequireAdmin is not defined, so it's filtered out (no error)
142149
}
143150
```
144151

145152
**Parameters:**
146-
- `key` (string) - The data key
147-
- `value` (object) - The data value
153+
154+
- `key` (string) - The data key
155+
- `value` (object) - The data value
148156

149157
**Note:** Route data is automatically filtered based on component parameters. You can use any `[RouteData]` attributes without needing matching parameters in the component - only data with matching `[Parameter]` properties will be passed through.
150158

@@ -162,7 +170,8 @@ public partial class OldPathRedirect : ComponentBase
162170
```
163171

164172
**Parameters:**
165-
- `redirectPath` (string) - The target redirect path
173+
174+
- `redirectPath` (string) - The target redirect path
166175

167176
**Note:** When using `RouteRedirect`, the component will not be rendered, and other attributes like `RouteTransition` or `RouteGuard` will be ignored.
168177

@@ -180,10 +189,37 @@ public partial class ProductsPage : ComponentBase
180189
```
181190

182191
**Parameters:**
183-
- `exact` (bool) - Whether the route path must match the URL exactly (defaults to `true`)
192+
193+
- `exact` (bool) - Whether the route path must match the URL exactly (defaults to `true`)
184194

185195
**Note:** When `false` (default behavior without the attribute), routes with child routes can match partially. Use `[RouteExact(true)]` for routes that should only match the exact path.
186196

197+
### `[RouteCache]` - Control Route Caching
198+
199+
Specifies whether caching is enabled for this specific route.
200+
201+
```csharp
202+
[Route("/admin/dashboard")]
203+
[RouteCache(false)]
204+
public partial class AdminDashboard : ComponentBase
205+
{
206+
// This route will never be cached
207+
}
208+
209+
[Route("/static-page")]
210+
[RouteCache(true)]
211+
public partial class StaticPage : ComponentBase
212+
{
213+
// This route will always be cached, even if global caching is disabled
214+
}
215+
```
216+
217+
**Parameters:**
218+
219+
- `enableCache` (bool?) - Whether to cache this route. `true` = always cache, `false` = never cache, `null` = use global settings (default)
220+
221+
**Note:** This affects route match caching. For more information about caching, see [CACHING.md](CACHING.md).
222+
187223
## Usage
188224

189225
### Basic Example
@@ -231,7 +267,7 @@ namespace MyApp.Pages
231267
// Access route data via parameters if needed
232268
[Parameter]
233269
public string? Section { get; set; }
234-
270+
235271
// Component implementation
236272
}
237273
}
@@ -283,7 +319,7 @@ using Blazouter.Extensions;
283319

284320
public partial class App
285321
{
286-
private List<RouteConfig> _routes =
322+
private List<RouteConfig> _routes =
287323
RouteConfigExtensions.FromAttributes(typeof(App).Assembly);
288324
}
289325
```
@@ -310,9 +346,9 @@ Attribute-based routes support dynamic parameters just like programmatic routes:
310346
public class UserDetailPage : ComponentBase
311347
{
312348
[Inject] private RouterStateService RouterState { get; set; } = default!;
313-
349+
314350
private string? _userId;
315-
351+
316352
protected override void OnInitialized()
317353
{
318354
_userId = RouterState.GetParam("id");
@@ -349,7 +385,7 @@ public class DataPreloadMiddleware : IRouteMiddleware
349385
// Preload data
350386
var data = await LoadDataAsync();
351387
context.Data["PreloadedData"] = data;
352-
388+
353389
await next();
354390
}
355391
}
@@ -361,7 +397,7 @@ public class UserDetailPage : ComponentBase
361397
{
362398
[Parameter]
363399
public object? PreloadedData { get; set; }
364-
400+
365401
// Only define parameters you need - other middleware data is automatically filtered
366402
}
367403
```
@@ -443,16 +479,18 @@ public class AdminPage : ComponentBase { }
443479
### 3. When to Use Attributes vs Programmatic
444480

445481
**Use Attributes When:**
446-
- Route configuration is simple and self-contained
447-
- You want configuration co-located with the component
448-
- The route doesn't have complex nested children
449-
- You need basic redirects or exact matching
482+
483+
- Route configuration is simple and self-contained
484+
- You want configuration co-located with the component
485+
- The route doesn't have complex nested children
486+
- You need basic redirects or exact matching
450487

451488
**Use Programmatic Configuration When:**
452-
- You need nested routes with complex hierarchies
453-
- Routes need to be generated dynamically
454-
- You need lazy loading with `ComponentLoader`
455-
- Route configuration is shared across components
489+
490+
- You need nested routes with complex hierarchies
491+
- Routes need to be generated dynamically
492+
- You need lazy loading with `ComponentLoader`
493+
- Route configuration is shared across components
456494

457495
**Note:** As of this version, all `RouteConfig` properties except `ComponentLoader` and `Children` are supported via attributes.
458496

@@ -474,7 +512,7 @@ private List<RouteConfig> _routes = new List<RouteConfig>
474512
new RouteConfig { Path = ":id", Component = typeof(ProductDetail) }
475513
}
476514
},
477-
515+
478516
// Lazy-loaded route - programmatic
479517
new RouteConfig
480518
{
@@ -521,22 +559,23 @@ Both approaches produce the exact same result - choose based on your preference
521559

522560
The following table shows which `RouteConfig` properties are supported via attributes:
523561

524-
| RouteConfig Property | Attribute Support | Attribute Name | Notes |
525-
|---------------------|-------------------|----------------|-------|
526-
| `Path` | ✅ Yes | `[Route("/path")]` | Required for attribute-based routing |
527-
| `Component` | ✅ Yes | (Inferred) | Automatically set to the decorated component type |
528-
| `Transition` | ✅ Yes | `[RouteTransition(...)]` | Supports all transition types |
529-
| `Middleware` | ✅ Yes | `[RouteMiddleware(typeof(...))]` | Can be applied multiple times |
530-
| `Guards` | ✅ Yes | `[RouteGuard(typeof(...))]` | Can be applied multiple times |
531-
| `Title` | ✅ Yes | `[RouteTitle("...")]` | Sets the route title |
532-
| `Layout` | ✅ Yes | `[RouteLayout(typeof(...))]` | Supports null for no layout |
533-
| `Data` | ✅ Yes | `[RouteData("key", value)]` | Can be applied multiple times |
534-
| `RedirectTo` | ✅ Yes | `[RouteRedirect("/path")]` | Component won't render when redirecting |
535-
| `Exact` | ✅ Yes | `[RouteExact(true)]` | Controls exact path matching |
536-
| `ComponentLoader` | ❌ No | N/A | Requires async lambda - use programmatic config |
537-
| `Children` | ❌ No | N/A | Complex hierarchies - use programmatic config |
538-
539-
**Coverage:** 10 out of 12 `RouteConfig` properties are supported via attributes (83% coverage).
562+
| RouteConfig Property | Attribute Support | Attribute Name | Notes |
563+
| -------------------- | ----------------- | -------------------------------- | ------------------------------------------------- |
564+
| `Path` | ✅ Yes | `[Route("/path")]` | Required for attribute-based routing |
565+
| `Component` | ✅ Yes | (Inferred) | Automatically set to the decorated component type |
566+
| `Transition` | ✅ Yes | `[RouteTransition(...)]` | Supports all transition types |
567+
| `Middleware` | ✅ Yes | `[RouteMiddleware(typeof(...))]` | Can be applied multiple times |
568+
| `Guards` | ✅ Yes | `[RouteGuard(typeof(...))]` | Can be applied multiple times |
569+
| `Title` | ✅ Yes | `[RouteTitle("...")]` | Sets the route title |
570+
| `Layout` | ✅ Yes | `[RouteLayout(typeof(...))]` | Supports null for no layout |
571+
| `Data` | ✅ Yes | `[RouteData("key", value)]` | Can be applied multiple times |
572+
| `RedirectTo` | ✅ Yes | `[RouteRedirect("/path")]` | Component won't render when redirecting |
573+
| `Exact` | ✅ Yes | `[RouteExact(true)]` | Controls exact path matching |
574+
| `EnableCache` | ✅ Yes | `[RouteCache(true/false)]` | Controls per-route caching |
575+
| `ComponentLoader` | ❌ No | N/A | Requires async lambda - use programmatic config |
576+
| `Children` | ❌ No | N/A | Complex hierarchies - use programmatic config |
577+
578+
**Coverage:** 11 out of 12 `RouteConfig` properties are supported via attributes (92% coverage).
540579

541580
The two unsupported properties (`ComponentLoader` and `Children`) require complex programmatic logic that cannot be expressed declaratively through attributes. For these scenarios, use traditional programmatic `RouteConfig` objects.
542581

@@ -549,8 +588,9 @@ The two unsupported properties (`ComponentLoader` and `Children`) require comple
549588
### Q: Can I use both `@page` and `[Route]` on the same component?
550589

551590
**A:** While technically possible, it's not recommended. Choose one routing approach:
552-
- Use `@page` for Blazor's built-in routing
553-
- Use `[Route]` for Blazouter's enhanced routing
591+
592+
- Use `@page` for Blazor's built-in routing
593+
- Use `[Route]` for Blazouter's enhanced routing
554594

555595
### Q: Do attribute-based routes break existing code?
556596

@@ -578,6 +618,6 @@ Check out the [AttributeRouting.razor](samples/Blazouter.WebAssembly.Sample/Page
578618

579619
## Learn More
580620

581-
- [Main README](README.md)
582-
- [Sample Applications](samples/)
583-
- [Features Documentation](FEATURES.md)
621+
- [Main README](README.md)
622+
- [Sample Applications](samples/)
623+
- [Features Documentation](FEATURES.md)

0 commit comments

Comments
 (0)