Skip to content

Commit ee83946

Browse files
committed
docs(v1/v2/v3): add Recommended Middleware and Vite Helper pages
Port Recommended Middleware and Vite Helper from inertiacore.net/docs/core into all three versioned trees. The Advanced sidebars already reference these slugs; without the files the build errors out. Same body content across v1/v2/v3 with version-appropriate cross-reference paths for CSRF Protection, Shared Data, and Error Handling — v1 has csrf-protection under Advanced and shared-data under The Basics, while v2/v3 have them under Security and Data & Props respectively. Converted the upstream GitHub-flavored `> [!NOTE]` callout to <Note>, which rewriteAsides maps to Starlight's native <Aside type="note">.
1 parent 8eb59ca commit ee83946

6 files changed

Lines changed: 954 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
title: Recommended Middleware
3+
description: ASP.NET Core middleware InertiaCore recommends for the best experience.
4+
---
5+
6+
ASP.NET Core makes several middleware available out of the box. We can also add a couple of our own to enhance the experience.
7+
8+
InertiaCore recommends using the following middleware in your application:
9+
10+
1. [CSRF Protection (recommended)](#csrf-protection)
11+
2. [Session (required)](#session)
12+
3. [HandleInertiaRequests (optional)](#handleinertiarequests)
13+
4. [Exception Handling (optional)](#exception-handling)
14+
15+
## CSRF Protection
16+
17+
We strongly recommend using the CSRF protection middleware. Please see the [CSRF Protection](/v1/advanced/csrf-protection) documentation for more information.
18+
19+
## Session
20+
21+
For the best experience, we recommend using the session middleware. The session middleware is used to store state in a session. This is useful for storing state between requests. This may be ModelState, flash messages, or other state that you need to persist between requests.
22+
23+
```csharp
24+
// Program.cs
25+
26+
// Add services to the container.
27+
builder.Services.AddControllersWithViews()
28+
.AddSessionStateTempDataProvider();
29+
30+
builder.Services.AddSession(options =>
31+
{
32+
options.Cookie.Name = "App.Session";
33+
options.Cookie.HttpOnly = true;
34+
options.Cookie.IsEssential = true;
35+
options.IdleTimeout = TimeSpan.FromMinutes(30);
36+
});
37+
builder.Services.AddInertia();
38+
builder.Services.AddViteHelper();
39+
40+
// Later on...
41+
42+
app.UseStaticFiles(); // For static files, like our CSS and JavaScript files
43+
app.UseRouting();
44+
app.UseSession();
45+
// app.UseAuthentication(); // If you have authentication
46+
// app.UseAuthorization(); // If you have authorization
47+
app.UseInertia();
48+
49+
app.MapControllerRoute(
50+
name: "default",
51+
pattern: "{controller}/{action=Index}/{id?}");
52+
```
53+
54+
## HandleInertiaRequests
55+
56+
We recommend creating a custom HandleInertiaRequests middleware. This middleware is used to handle Inertia shared data on responses. This is helpful for flash messages, auth data, and other state that you want to include on every response. See the [Shared Data](/v1/the-basics/shared-data) documentation for more information.
57+
58+
<Note>
59+
This middleware is not required, but it is recommended for the best experience.
60+
It's worth noting every Inertia response will include the shared data, so it's important to only include the data that you need. You may want to cache heavy queries or make them lazy/optional to avoid performance issues. You can then `JSON.parse` out the `data-page` attribute on the `#app` element to get the data you need from the initial page load.
61+
</Note>
62+
63+
```csharp
64+
// Program.cs
65+
app.UseInertia();
66+
app.UseMiddleware<HandleInertiaRequests>();
67+
```
68+
69+
```csharp
70+
// HandleInertiaRequests.cs
71+
using InertiaCore;
72+
using Microsoft.AspNetCore.Identity;
73+
using Microsoft.AspNetCore.Mvc.ViewFeatures;
74+
using Microsoft.EntityFrameworkCore;
75+
using App.Data;
76+
using App.Models;
77+
78+
namespace App.Middleware;
79+
80+
public class HandleInertiaRequests
81+
{
82+
private readonly RequestDelegate _next;
83+
84+
public HandleInertiaRequests(RequestDelegate next)
85+
{
86+
_next = next;
87+
}
88+
89+
public async Task InvokeAsync(HttpContext context)
90+
{
91+
Inertia.Share("flash", () =>
92+
{
93+
try
94+
{
95+
var factory = context.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
96+
var tempData = factory.GetTempData(context);
97+
var flash = new Dictionary<string, object>();
98+
99+
if (tempData.TryGetValue("error", out var error) && error is not null)
100+
{
101+
// Ensure we only store simple serializable types
102+
flash.Add("error", error.ToString() ?? "");
103+
}
104+
if (tempData.TryGetValue("success", out var success) && success is not null)
105+
{
106+
// Ensure we only store simple serializable types
107+
flash.Add("success", success.ToString() ?? "");
108+
}
109+
return flash;
110+
}
111+
catch (Exception)
112+
{
113+
// If there's any issue with TempData, return empty flash to prevent crashes
114+
return new Dictionary<string, object>();
115+
}
116+
});
117+
118+
Inertia.Share("auth", () =>
119+
{
120+
if (context.User.Identity?.IsAuthenticated == true)
121+
{
122+
var userManager = context.RequestServices.GetRequiredService<UserManager<User>>();
123+
var dbContext = context.RequestServices.GetRequiredService<ApplicationDbContext>();
124+
var user = userManager.GetUserAsync(context.User).Result;
125+
if (user != null)
126+
{
127+
// Load the account data separately
128+
var account = dbContext.Accounts.Find(user.AccountId);
129+
130+
return (object)new
131+
{
132+
user = new
133+
{
134+
id = user.Id,
135+
first_name = user.FirstName,
136+
last_name = user.LastName,
137+
email = user.Email,
138+
owner = user.Owner,
139+
account = new
140+
{
141+
id = user.AccountId,
142+
name = account?.Name
143+
}
144+
}
145+
};
146+
}
147+
}
148+
return (object)new { user = (object?)null };
149+
});
150+
151+
await _next(context);
152+
}
153+
}
154+
```
155+
156+
## Exception Handling
157+
158+
We strongly recommend using the exception handling middleware. Please see the [Error Handling](/v1/advanced/error-handling) documentation for more information.

v1/advanced/vite-helper.mdx

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: Vite Helper
3+
description: Use the Vite helper to load your generated styles and scripts in InertiaCore.
4+
---
5+
6+
A Vite helper class is available to automatically load your generated styles or scripts by simply using the `@Vite.Input("src/main.tsx")` helper. You can also enable HMR when using React by using the `@Vite.ReactRefresh()` helper. This pairs well with the `@inertiacore/vite-plugin` npm package.
7+
8+
To get started with the Vite Helper, you will need to add one more line to the `Program.cs` or `Starup.cs` file.
9+
10+
```csharp
11+
using InertiaCore.Extensions;
12+
13+
[...]
14+
15+
builder.Services.AddViteHelper();
16+
17+
// Or with options (default values shown)
18+
19+
builder.Services.AddViteHelper(options =>
20+
{
21+
options.PublicDirectory = "wwwroot";
22+
options.BuildDirectory = "build";
23+
options.HotFile = "hot";
24+
options.ManifestFilename = "manifest.json";
25+
});
26+
```
27+
28+
Here are the default values for the Vite Plugin options:
29+
30+
```ts
31+
import { defineConfig } from "vite";
32+
import inertiacore from "@inertiacore/vite-plugin";
33+
34+
export default defineConfig({
35+
plugins: [
36+
//... other plugins,
37+
inertiacore({
38+
input: ["src/app.ts"],
39+
refresh: true,
40+
}),
41+
],
42+
});
43+
44+
// Same as above, but with default values
45+
export default defineConfig({
46+
plugins: [
47+
//... other plugins,
48+
inertiacore({
49+
input: ["src/app.ts"],
50+
refresh: true,
51+
publicDirectory: "../wwwroot",
52+
buildDirectory: "build",
53+
hotFile: "hot",
54+
manifestFilename: "manifest.json",
55+
}),
56+
],
57+
});
58+
```
59+
60+
## Examples
61+
62+
Here's an example for a TypeScript React app with HMR:
63+
64+
```html
65+
@using InertiaCore @using InertiaCore.Utils
66+
<!DOCTYPE html>
67+
<html>
68+
<head>
69+
<meta charset="utf-8" />
70+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
71+
<title inertia>My App</title>
72+
</head>
73+
<body>
74+
@* This has to go first, otherwise preamble error *@ @Vite.ReactRefresh()
75+
@await Inertia.Html(Model) @Vite.Input("src/main.tsx")
76+
</body>
77+
</html>
78+
```
79+
80+
with the corresponding `vite.config.js`, which is recommended to create in the `ClientApp` directory:
81+
82+
```js
83+
import { defineConfig } from "vite";
84+
import react from "@vitejs/plugin-react";
85+
import inertiacore from "@inertiacore/vite-plugin";
86+
87+
// https://vitejs.dev/config/
88+
export default defineConfig({
89+
plugins: [
90+
inertiacore({
91+
input: ["src/main.tsx"],
92+
}),
93+
react(),
94+
],
95+
build: {
96+
emptyOutDir: true,
97+
},
98+
});
99+
```
100+
101+
Here's an example for a TypeScript Vue app with Hot Reload:
102+
103+
```html
104+
@using InertiaCore @using InertiaCore.Utils
105+
<!DOCTYPE html>
106+
<html>
107+
<head>
108+
<meta charset="utf-8" />
109+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
110+
<title inertia>My App</title>
111+
</head>
112+
<body>
113+
@await Inertia.Html(Model) @Vite.Input("src/app.ts")
114+
</body>
115+
</html>
116+
```
117+
118+
with the corresponding `vite.config.js`, which is recommended to create in the `ClientApp` directory:
119+
120+
```js
121+
import { defineConfig } from "vite";
122+
import vue from "@vitejs/plugin-vue";
123+
import inertiacore from "@inertiacore/vite-plugin";
124+
125+
export default defineConfig({
126+
plugins: [
127+
inertiacore({
128+
input: ["src/app.ts"],
129+
refresh: true,
130+
}),
131+
vue({
132+
template: {
133+
transformAssetUrls: {
134+
base: null,
135+
includeAbsolute: false,
136+
},
137+
},
138+
}),
139+
],
140+
build: {
141+
emptyOutDir: true,
142+
},
143+
});
144+
```
145+
146+
Here's an example that just produces a single CSS file:
147+
148+
```html
149+
@using InertiaCore @using InertiaCore.Utils
150+
<!DOCTYPE html>
151+
<html>
152+
<head>
153+
<meta charset="utf-8" />
154+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
155+
</head>
156+
<body>
157+
@await Inertia.Html(Model) @Vite.Input("src/main.scss")
158+
</body>
159+
</html>
160+
```

0 commit comments

Comments
 (0)