Avoiding DLL issues in Sitecore XP projects

If you’ve ever worked on a Sitecore XP project, especially using the Helix patterns, you’ve probably experienced assembly version conflicts with your nuget package dependencies. Essentially, a package referenced in nuget is pulls in a dependency that overwrites an assembly that ships with Sitecore with a newer version, leading to an error.

Sometimes you just need to reference an assembly in your code, but not actually deploy it. At first glance there seems to be no way to do this in the nuget package manager. You can, however, do this directly in the .csproj file.

In this example, I needed to reference Microsoft.AspNet.Owin to work on a feature using federated authentication. When I published my project, it pulled in a bunch of other DLLs, the package’s transitive dependencies, that overwrote the ones Sitecore shipped with. In this example, the easiest solution is to tell MSBuild to not publish this package, since it’s already in the /bin folder. But, we still need to include this package for compilation.

The solution is to add <IncludeAssets>compile</IncludeAssets> to the .csproj in the package declaration. For example, with my Microsoft.AspNet.Owin package, the package declaration would look like this:

<PackageReference Include="Microsoft.AspNet.Identity.Owin">
  <Version>2.2.4</Version>
  <IncludeAssets>compile</IncludeAssets>
</PackageReference>

Now when I publish my project, the assembly and its dependencies (Newtonsoft.Json anyone?) do not publish and overwrite the stock assemblies. For mor info on how to configure package behavior, see this article: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files

Throttling API endpoints in Sitecore XP with MvcThrottle

In a recent Sitecore XP project, we built a controller route that generates PDFs of the pages on demand. While this worked well functionally, we realized that without any rate limiting, users (or bots) could easily overload the server by repeatedly hitting the endpoint. To prevent performance issues by overloading the server, we needed a simple way to throttle requests to this route.

The MvcThrottle Module

After exploring a few options, we landed on the MvcThrottle module that allows you to limit the number of HTTP requests a client can make to your Web API or MVC endpoints over a specified time window.

MvcThrottle tracks incoming requests based on client IP, endpoint, or custom keys, and enforces rate limits using in-memory or distributed stores. You can configure limits per second, minute, hour, or day, and when a limit is exceeded return a 429 Too Many Requests response.

Implementation in Sitecore XP

Here’s how we integrated MvcThrottle into my Sitecore XP solution:

  1. Add the NuGet Package
    I added the MvcThrottle package to the Foundation layer of my solution, in this case the Foundation module handling Mvc customizations, using NuGet:
Install-Package MvcThrottle
  1. Register the MvcThrottle Module
    MvcThrottle is typically registered in Global.asax, but since this is a Sitecore solution, I registered it via the Sitecore initialize pipeline for modularity and to avoid global configurations
using System.Web.Mvc;
using Sitecore.Pipelines;
using MvcThrottle;

namespace MyProject.Foundation.Mvc.Pipelines.Initialize
{
  public class RegisterMvcThrottle
  {
    public void Process(PipelineArgs args)
    {
      var throttleFilter = new ThrottlingFilter
      {
        Policy = new ThrottlePolicy(
          // Required parameter, set to null if not needed
          perSecond: null,    
          perMinute: 5,
          perHour: 100)
        {
          IpThrottling = true
        },
        Repository = new CacheRepository()
      };

      GlobalFilters.Filters.Add(throttleFilter);
    }
  }
}
  1. Apply the MvcThrottle Attribute
    Next, I decorated the PDF generation controller action with the [EnableThrottling] attribute to enforce rate limits:
[HttpGet]
[EnableThrottling]
public ActionResult Pdf(string id)
{
  // Pdf generation logic...
}

In this case we’re using the defaults configured in the module registration. If desired, you can set custom throttling parameters on each action by passing parameters with the EnableThrottling attribute.

  1. Handle 429 Errors
    Finally, we need to handle the error thrown when a throttling limit is reached. This should map to a page in your site, like the 404 page, with a useful error. If you have a Foundation error handling module, you can handle it there. The simplest approach is to configure a custom error page for HTTP 429 responses in web.config:

<system.webServer>
  <httpErrors errorMode="Custom">
    <error statusCode="429" path="/error/ratelimit" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Summary

MvcThrottle provided a quick and effective way to protect my PDF generation route from abuse. It’s easy to integrate, works well with Sitecore, and offers enough flexibility for most throttling scenarios. In this example we’re throttling by IP. It’s possible to configure this differently by changing the caching strategy. For example, if your site requires authentication, you could throttle by user id instead.

.NET not mapping fields with spaces in the headless SDK

TLDR: Don’t put spaces in your field names for Sitecore headless builds.

Quick post about a bug I uncovered today working on an XM Cloud project with a .NET rendering host.

We’re seeing inconsistent behavior when mapping layout service field data to types in .NET. In short, fields with spaces in the name sometimes do not deserialize into .NET types when you attempt to do so with the SDK.

Consider a page with 2 fields: Topic and Content Type. In our code, we have a class that looks like this:

namespace MySite.Models.Foundation.Base
{
  public class PageBase
  {
    [SitecoreComponentField(Name = "Content Type")]
    public ItemLinkField<Enumeration>? ContentType { get; set; }

    [SitecoreComponentField(Name = "Topic")]
    public ContentListField<Enumeration>? Topic { get; set; }
  }
}

When I create a component as a ModelBoundView,
.AddModelBoundView<PageBase>("PageHeader")
the fields map properly, and I get data in ContentType and Topic properties of my model.

When I try to map it from a service, like so:

namespace MySite.Services.SEO
{
  public class MetadataService
  {
    private SitecoreLayoutResponse? _response;

    public PageMetadata GetPageMetadata(SitecoreLayoutResponse? response)
    {
      _response = response;
      PageBase pageBase= new PageBase ();
      var sitecoreData = _response.Content.Sitecore;

      sitecoreData.Route?.TryReadFields<PageBase>(out pageBase);
      return pageMetadata;
    }
  }
}

I get no data in the Content Type field but I do in the Topic field. If I rename Content Type to ContentType, the field data is bound to the ContentType property as expected.

I dug into the code a little bit and it seems that the HandleReadFields method on the Sitecore.LayoutService.Client.Response.Model.FieldsReader is ignoring the property attributes: [SitecoreComponentField(Name = "Content Type")]
Instead it is just using the property name, which of course has no spaces in it because it’s a C# identifier.

Until this bug is corrected, the workaround is to rename your fields to not have spaces in them.