Skip to main content
The Intent.AspNetCore.Mvc module generates ASP.NET Core MVC Controller classes for applications that require server-side rendering with Razor views, as opposed to pure API controllers.
This module is currently in beta (version 1.0.0-beta.11). For REST API applications, use Intent.AspNetCore.Controllers instead.

Overview

While the standard Controllers module generates API controllers for JSON responses, the MVC module generates traditional MVC controllers that work with Razor views to return HTML. This is ideal for server-rendered web applications.

What Gets Generated

MVC Controller

Generated MVC controllers return views instead of JSON:
public class ProductsController : Controller
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet]
    public async Task<IActionResult> Index(CancellationToken cancellationToken)
    {
        var products = await _productService.GetAllAsync(cancellationToken);
        return View(products);
    }

    [HttpGet]
    public async Task<IActionResult> Details(Guid id, CancellationToken cancellationToken)
    {
        var product = await _productService.GetByIdAsync(id, cancellationToken);
        if (product == null)
            return NotFound();

        return View(product);
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(
        CreateProductDto dto,
        CancellationToken cancellationToken)
    {
        if (!ModelState.IsValid)
            return View(dto);

        await _productService.CreateAsync(dto, cancellationToken);
        return RedirectToAction(nameof(Index));
    }

    [HttpGet]
    public async Task<IActionResult> Edit(Guid id, CancellationToken cancellationToken)
    {
        var product = await _productService.GetByIdAsync(id, cancellationToken);
        if (product == null)
            return NotFound();

        return View(product);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(
        Guid id,
        UpdateProductDto dto,
        CancellationToken cancellationToken)
    {
        if (!ModelState.IsValid)
            return View(dto);

        await _productService.UpdateAsync(id, dto, cancellationToken);
        return RedirectToAction(nameof(Index));
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
    {
        await _productService.DeleteAsync(id, cancellationToken);
        return RedirectToAction(nameof(Index));
    }
}

View Stubs

The module generates stub view files for each controller action:
@model ProductDto

@{
    ViewData["Title"] = "Product Details";
}

<h1>@ViewData["Title"]</h1>

<div>
    <h4>Product</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">@Html.DisplayNameFor(model => model.Name)</dt>
        <dd class="col-sm-10">@Html.DisplayFor(model => model.Name)</dd>
        
        <dt class="col-sm-2">@Html.DisplayNameFor(model => model.Price)</dt>
        <dd class="col-sm-10">@Html.DisplayFor(model => model.Price)</dd>
    </dl>
</div>

<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Key Features

View-Based

Returns HTML views using Razor

Form Handling

Built-in support for form posts and model binding

Anti-Forgery

Automatic CSRF protection

Validation

ModelState validation integration

MVC vs API Controllers

FeatureMVC ControllersAPI Controllers
Primary UseServer-rendered HTMLREST APIs returning JSON
Base ClassControllerControllerBase
ReturnsIActionResult with ViewsActionResult<T> with JSON
Anti-ForgeryRequired for POSTNot required
Content NegotiationReturns HTMLReturns JSON/XML
ModuleIntent.AspNetCore.MvcIntent.AspNetCore.Controllers

When to Use MVC Controllers

Choose MVC controllers when:
Your application needs to generate HTML on the server using Razor views
Building a classic web application with pages and navigation
Search engines need to crawl fully-rendered HTML content
You’re not using React, Angular, or Vue for the frontend

View Structure

The module generates views organized by controller:
Views/
├── Products/
│   ├── Index.cshtml
│   ├── Details.cshtml
│   ├── Create.cshtml
│   ├── Edit.cshtml
│   └── Delete.cshtml
├── Customers/
│   ├── Index.cshtml
│   └── ...
├── Shared/
│   ├── _Layout.cshtml
│   └── _ValidationScriptsPartial.cshtml
└── _ViewStart.cshtml

Form Handling

MVC controllers handle form submissions with model binding:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [FromForm] CreateProductDto dto,
    CancellationToken cancellationToken)
{
    // Validate the model
    if (!ModelState.IsValid)
    {
        // Return to the form with validation errors
        return View(dto);
    }

    // Process the form
    var productId = await _productService.CreateAsync(dto, cancellationToken);

    // Redirect to success page
    return RedirectToAction(nameof(Details), new { id = productId });
}
Corresponding Razor form:
@model CreateProductDto

<form asp-action="Create" method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    
    <div class="form-group">
        <label asp-for="Name" class="control-label"></label>
        <input asp-for="Name" class="form-control" />
        <span asp-validation-for="Name" class="text-danger"></span>
    </div>
    
    <div class="form-group">
        <label asp-for="Price" class="control-label"></label>
        <input asp-for="Price" class="form-control" />
        <span asp-validation-for="Price" class="text-danger"></span>
    </div>
    
    <div class="form-group">
        <input type="submit" value="Create" class="btn btn-primary" />
    </div>
</form>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Service Contract Dispatch

MVC controllers use service contract dispatch to interact with your application layer:
public class ProductsController : Controller
{
    private readonly IProductService _service;
    private readonly IUnitOfWork _unitOfWork;

    public ProductsController(
        IProductService service,
        IUnitOfWork unitOfWork)
    {
        _service = service;
        _unitOfWork = unitOfWork;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(
        CreateProductDto dto,
        CancellationToken cancellationToken)
    {
        if (!ModelState.IsValid)
            return View(dto);

        await _service.CreateAsync(dto, cancellationToken);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return RedirectToAction(nameof(Index));
    }
}

Installation

Intent.AspNetCore.Mvc

Dependencies

  • Intent.Application.Contracts (>= 5.0.4)
  • Intent.Common.CSharp
  • Intent.Modelers.Services
  • Intent.OutputManager.RoslynWeaver

Hybrid Applications

You can use both MVC and API controllers in the same application. Install both modules to have some endpoints return views and others return JSON.
Example hybrid setup:
// MVC Controller for admin pages
public class AdminProductsController : Controller
{
    // Returns HTML views
}

// API Controller for mobile app
[ApiController]
[Route("api/products")]
public class ProductsApiController : ControllerBase
{
    // Returns JSON
}

Next Steps

API Controllers

Generate REST API controllers instead

Razor Pages

Consider Razor Pages for simpler scenarios

Blazor

Explore Blazor for component-based UIs

Build docs developers (and LLMs) love