Skip to content

XCon Studio Vite Proxy (.NET)

XCon Studio Vite Proxy is a middleware that provides seamless integration with Vite development server in ASP.NET Core applications. It automatically starts, manages and proxies the Vite server during development.

⚠️ Note: This middleware only works in Development environment. A separate build system is used for production.

🚀 Installation

Installation with NuGet

bash
# With .NET CLI
dotnet add package XConViteProxy

# In Package Manager Console
Install-Package XConViteProxy

NuGet Package: https://www.nuget.org/packages/XConViteProxy

Package Reference

xml
<PackageReference Include="XConViteProxy" Version="1.0.31" />

🎯 Quick Start

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllersWithViews();

// Add Razor runtime compilation for hot reload
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddRazorPages()
        .AddRazorRuntimeCompilation();
}

// XCon Vite Proxy hosted service
builder.Services.AddXConViteProxy();

var app = builder.Build();

// Configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
else
{
    // XCon Vite Proxy Middleware - MUST be before UseStaticFiles
    app.UseXConViteProxy(app.Environment);
    app.UseDeveloperExceptionPage();
}

app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Important Points:

  • AddXConViteProxy() adds the service to DI
  • UseXConViteProxy() middleware should be added BEFORE UseStaticFiles()
  • ✅ Hot reload support with Razor Runtime Compilation
  • ✅ Only active in Development environment

That's it! Now when you press F5:

  • Vite dev server starts automatically
  • /scripts/* requests are routed to Vite
  • HMR (Hot Module Replacement) works
  • Razor views also update with hot reload
  • Vite closes automatically when the application closes

Usage with Custom Port

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Custom Vite server URL
app.UseXConViteProxy(app.Environment, "http://localhost:5173");

app.Run();

⚙️ Automatic Configuration

The middleware automatically performs the following steps:

1. Reading Dev Script from package.json

json
{
  "scripts": {
    "dev": "vite --config vite.config.dev.js"
  }
}

2. Parsing Vite Config File

javascript
// vite.config.dev.js
export default defineConfig({
  root: path.resolve(__dirname, 'scripts'),
  server: {
    port: 4201
  }
});

3. Automatic Detection

The middleware automatically detects:

  • ✅ Vite config file path (from --config parameter)
  • ✅ Root folder (from root: setting)
  • ✅ Port number (from port: setting)
  • ✅ Development command (npm run dev)

🔧 Working Principle

Vite Server Management

csharp
// Middleware does the following:

// 1. At Startup
- Reads package.json
- Parses Vite config
- Runs `npm run dev` command
- Waits for Vite to be ready

// 2. At Runtime
- Captures requests to Vite root path
- Proxies to Vite server
- Adds CORS headers
- Routes HMR websocket connections

// 3. At Shutdown
- Cleans up Vite process
- Terminates child processes
- Releases resources

Process Safety (Windows)

Process safety is ensured using Job Object on Windows:

csharp
// Windows Job Object features:
- Vite closes automatically if main process crashes
- No orphan processes remain
- No resource leaks
- Clean shutdown guarantee

🗂️ Project Structure

A typical ASP.NET Core + Vite project:

MyWebApp/
├── Program.cs                    # ASP.NET Core entry point
├── appsettings.json
├── MyWebApp.csproj
├── package.json                  # npm/vite configuration
├── vite.config.dev.js           # Vite development config
├── scripts/                      # Vite root folder
│   ├── src/
│   │   ├── main.ts
│   │   ├── components/
│   │   │   ├── my-widget/
│   │   │   │   ├── my-widget.ts
│   │   │   │   ├── my-widget.html
│   │   │   │   └── my-widget.css
│   │   └── app.ts
│   ├── index.html
│   └── vite-env.d.ts
└── wwwroot/
    └── index.html               # ASP.NET static files

⚠️ Important: Middleware Ordering

Critical: UseXConViteProxy() middleware MUST be added BEFORE UseStaticFiles()!

✅ Correct Ordering

csharp
if (builder.Environment.IsDevelopment())
{
    // 1. FIRST Vite Proxy
    app.UseXConViteProxy(app.Environment);
    app.UseDeveloperExceptionPage();
}

// 2. THEN Static Files
app.UseStaticFiles();
app.UseRouting();

❌ Wrong Ordering

csharp
// WRONG! If static files come first, Vite proxy won't work
app.UseStaticFiles();
app.UseXConViteProxy(app.Environment);  // Never called!

Why? Middlewares run in order. If UseStaticFiles() finds a file in the /scripts folder, it terminates the request and never reaches the Vite proxy.

📋 Supported Features

Automatic Proxy Paths

The middleware automatically proxies these paths:

csharp
// Vite root path
/scripts/*                    → Vite server

// Vite special paths
/@vite/*                      → Vite internal
/@fs/*                        → File system access
/node_modules/*               → Dependencies
/@id/*                        → Module IDs

// Vite file types
*.ts, *.tsx, *.jsx, *.js     → TypeScript/JavaScript
*.css, *.scss, *.less        → Styles
*.vue, *.svelte              → Framework files

CORS Support

Automatic CORS headers are added:

csharp
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type

🚀 Usage Examples

Basic MVC Application

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);

// MVC services
builder.Services.AddControllersWithViews();

// Razor runtime compilation (for hot reload)
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddRazorPages()
        .AddRazorRuntimeCompilation();
}

// XCon Vite Proxy hosted service
builder.Services.AddXConViteProxy();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
else
{
    // IMPORTANT: Vite proxy must be BEFORE UseStaticFiles
    app.UseXConViteProxy(app.Environment);
    app.UseDeveloperExceptionPage();
}

app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

With Razor Pages

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

var app = builder.Build();

// Vite proxy (development only)
app.UseXConViteProxy(app.Environment);

app.MapRazorPages();

app.Run();

API + SPA (Vite) Structure

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);

// API services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Development middlewares
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
    
    // Vite dev server proxy
    app.UseXConViteProxy(app.Environment);
}

// API endpoints
app.MapControllers();

// SPA fallback
app.MapFallbackToFile("index.html");

app.Run();

Adding Custom Vite Service

csharp
using XCons.ViteProxy;

var builder = WebApplication.CreateBuilder(args);

// Manually add Vite proxy service
builder.Services.AddXConViteProxy();

var app = builder.Build();

// Middleware usage
if (app.Environment.IsDevelopment())
{
    app.UseXConViteProxy(app.Environment, "http://localhost:4201");
}

app.Run();

📝 Console Output and Logging

Vite Output

Vite server outputs appear in console:

bash
XCon Vite Proxy: Starting Vite dev server...
XCon Vite Proxy: Vite dev server started successfully
XCon Vite Proxy: Using Vite root path: /scripts
[Vite] VITE v5.0.0  ready in 500 ms
[Vite] ➜  Local:   http://localhost:4201/
[Vite] ➜  Network: use --host to expose

Proxy Logging

bash
XCon Vite Proxy: Service constructed, URL: http://localhost:4201
XCon Vite Proxy: Detected port from config: 4201
XCon Vite Proxy: Detected root: /scripts
XCon Vite Proxy: Vite started (PID: 12345)
XCon Vite Proxy: Vite process (PID: 12345) assigned to Job Object ✓
XCon Vite Proxy: Child will be killed automatically when parent dies
XCon Vite Proxy: Vite ready

Error Situations

bash
# Config not found
XCon Vite Proxy: package.json not found
XCon Vite Proxy: Using default: /scripts, port: 4201

# Vite could not be started
XCon Vite Proxy: Failed to start Vite process
XCon Vite Proxy: Error: npm not found in PATH

# Proxy error
XCon Vite Proxy: Proxy error: Connection refused
HTTP 502: Vite proxy error: Unable to connect to Vite server

🛠 Troubleshooting

Problem: Vite Server Not Starting

Solution:

bash
# Make sure Node.js and npm are installed
node --version
npm --version

# Check that dev script exists in package.json
npm run dev

# Make sure dependencies are installed
npm install

Problem: Port Conflict

Solution:

csharp
// Use custom port
app.UseXConViteProxy(app.Environment, "http://localhost:5173");

Or change port in vite.config.js:

javascript
export default defineConfig({
  server: {
    port: 5173  // Different port
  }
});

Problem: Hot Reload Not Working

Solution:

javascript
// vite.config.js - HMR settings
export default defineConfig({
  server: {
    port: 4201,
    hmr: {
      protocol: 'ws',
      host: 'localhost',
      port: 4201
    }
  }
});

Problem: CORS Errors

Solution:

csharp
// Middleware automatically adds CORS headers
// If problem persists:

app.UseCors(policy => 
    policy.AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader());

app.UseXConViteProxy(app.Environment);

Problem: Process Not Closing (Orphan Process)

Solution: Job Object works automatically on Windows. If you experience issues:

powershell
# Clean orphan Vite processes
Get-Process | Where-Object {$_.ProcessName -like "*node*"} | Stop-Process -Force

⚙️ Advanced Configuration

Multiple Vite Instances

csharp
// For main widgets
app.UseXConViteProxy(app.Environment, "http://localhost:4201");

// Another Vite for admin panel
app.UseMiddleware<XConViteMiddleware>("/admin-scripts", "http://localhost:4202");

Custom Path Matching

csharp
public class CustomViteMiddleware : XConViteMiddleware
{
    public CustomViteMiddleware(RequestDelegate next, string viteRootPath) 
        : base(next, viteRootPath) { }
    
    protected override bool ShouldProxyToVite(string path)
    {
        // Custom path logic
        return path.StartsWith("/custom-path") || 
               base.ShouldProxyToVite(path);
    }
}

Conditional Vite Startup

csharp
var builder = WebApplication.CreateBuilder(args);

// Add Vite service
builder.Services.AddXConViteProxy();

var app = builder.Build();

// Start only under certain conditions
if (app.Environment.IsDevelopment() && 
    builder.Configuration.GetValue<bool>("EnableVite"))
{
    app.UseXConViteProxy(app.Environment);
}

app.Run();

🔒 Security Notes

Development-Only Usage

csharp
// ✅ Correct - Only in development
if (app.Environment.IsDevelopment())
{
    app.UseXConViteProxy(app.Environment);
}

// ❌ Wrong - Don't use in production
app.UseXConViteProxy(app.Environment); // Always runs!

Safe Port Management

csharp
// Read from appsettings.Development.json
var viteUrl = builder.Configuration["Vite:Url"] ?? "http://localhost:4201";

if (app.Environment.IsDevelopment())
{
    app.UseXConViteProxy(app.Environment, viteUrl);
}

Process Safety

csharp
// Process safety with Job Object (Windows)
// - Prevents orphan processes
// - Prevents resource leaks
// - Clean shutdown guarantee

// Process cleanup also works on Linux/macOS
// ViteDevServerService.Dispose() does automatic cleanup

💡 Tip: With XCon Vite Proxy, the ASP.NET Core + Vite development experience becomes extremely simple. All you need to do is add the line app.UseXConViteProxy(app.Environment); - everything else is automatic!