This project is a UVE .NET example/base project and is intended to be used as a template for web projects looking to render dotCMS content and pages using .NET MVC and Razor templates. The example points to our demo site and is pulling the page, content, layout grid and other information from https://demo.dotcms.com
To run the application locally against the demo site with the default settings, use:
dotnet runThe application will be available at https://localhost:5001.
https://github.com/user-attachments/assets/8cdf8952-63bf-4214-af85-f0c1eed3a32c
This application follows the Model-View-Controller (MVC) architectural pattern to render dotCMS content:
DotCmsUVEController)#The DotCmsUVEController acts as a catch-all controller that intercepts all incoming requests:
[Route("{**catchAll}")]
public async Task<IActionResult> Index(string catchAll, ...)mode, language_id, personaIdIDotCmsService to fetch page data from dotCMSThe application uses strongly-typed C# models that mirror dotCMS data structures:
PageResponse: The main response model containing all page dataPage: Metadata about the page (title, tags, permissions)Layout: Defines the page structure (rows, columns, containers)Container: Represents content areas that can hold contentletsContentlet: Individual pieces of content with their propertiesDotCmsService)#The service layer handles all communication with the dotCMS API:
public async Task<PageResponse> GetPageAsync(PageQueryParams queryParams)
{
// 1. Build API URL with parameters
// 2. Add authentication headers
// 3. Execute HTTP request
// 4. Deserialize JSON response to C# models
// 5. Cache response for performance
}Key features:
For more details, see DotCmsService Documentation.
The Razor view engine renders the dotCMS page structure:
Views/DotCmsView/Index.cshtml)#@model RazorPagesDotCMS.Models.PageResponse
@foreach (var row in layout.Body.Rows)
{
<section class="section">
@foreach (var column in row.Columns)
{
<div class="col-lg-@column.Width">
@foreach (var container in column.Containers)
{
<!-- Render container and its contentlets -->
<contentlet-renderer contentlet="contentlet" />
}
</div>
}
</section>
}The view:
Views/DotCmsView/ContentTypes/)#Each content type has its own Razor view for custom rendering:
<!-- Banner.cshtml -->
@model Contentlet
<div class="banner">
<img src="https://raw.githubusercontent.com/dotCMS/dotnet-starter-example/main/@Model.GetProperty("image")" alt="@Model.GetProperty("altText")">
<h2>@Model.Title</h2>
<p>@Html.Raw(Model.GetProperty("description"))</p>
</div>Custom TagHelpers simplify content rendering:
[HtmlTargetElement("contentlet-renderer")]
public class ContentletTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// 1. Determine content type
// 2. Find appropriate view (e.g., Banner.cshtml)
// 3. Render contentlet using the view
}
}This allows simple usage in views:
<contentlet-renderer contentlet="@myContentlet" />These TagHelpers provide reusable header and footer components that can be conditionally displayed:
<!-- Basic usage with default content from partial views -->
<header-section show="@(layout?.Header == true)" title="My Site"></header-section>
<footer-section show="@(layout?.Footer == true)" copyright="My Site"></footer-section>
<!-- With custom content -->
<header-section show="@(layout?.Header == true)">
<div class="custom-header">
<h1>Welcome to My Site</h1>
<nav><!-- Navigation items --></nav>
</div>
</header-section>/about-usDotCmsUVEController catches the requestDotCmsService.GetPageAsync("/about-us") is calledThe application is configured through the appsettings.json file:
{
"dotCMS": {
"ApiHost": "https://demo.dotcms.com",
"ApiToken": "ABC123......",
"ApiUserName": "admin@dotcms.com",
"ApiPassword": "admin",
"CacheTTL": 120
},
"proxy": [
{
"enabled": true,
"path": "/dA*",
"target": "https://demo.dotcms.com/dA"
},
{
"enabled": true,
"path": "/contentAsset*",
"target": "https://demo.dotcms.com/contentAsset"
}
]
}The .NET starter contains a static asset proxy that automatically forwards requests that start with /dA or /contentAsset to dotCMS for servicing. This is particularly useful when you are running behind a CDN or some other layer and do not want to deal with remembering which origin to use, one for page requests and another for assets and images.
For more details, see Proxy Documentation.
This MVC + Razor approach provides several advantages:
This is a WIP and there is still a lot to do for this example to be complete. These include:
data-attr for UVE and containers, contentView under /Views/ContentTypes/{containerName}/{ContentType}.cshtml and then fall back to /Views/ContentTypes/{ContentType}.cshtml if a container specific view is not availableFound an issue with this documentation? View the source