Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
32ea766
spelling: UrlProviderCollecton -> UrlProviderCollection
liamlaverty Sep 3, 2025
6f07b56
spelling: infite -> infinite
liamlaverty Sep 3, 2025
e1bd877
spelling: filese -> files
liamlaverty Sep 3, 2025
b058597
spelling: WithMiddelware -> WithMiddleware
liamlaverty Sep 3, 2025
5d4ff93
spelling: wich -> which
liamlaverty Sep 3, 2025
ab4fc45
spelling: propperty -> property
liamlaverty Sep 3, 2025
17291d0
spelling: abouth -> about
liamlaverty Sep 3, 2025
688ebb9
spelling: remove `zspo`
liamlaverty Sep 3, 2025
785a75b
spelling: extention -> extension
liamlaverty Sep 3, 2025
1f2770e
spelling: enabeld -> enabled
liamlaverty Sep 3, 2025
1a31ae5
spelling: overriden -> overridden
liamlaverty Sep 3, 2025
eca8ac7
Update 10/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
d74df64
Update 10/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
838c4f4
Update 10/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
751b31e
Update 10/umbraco-cms/reference/routing/request-pipeline/published-co…
eshanrnh Sep 4, 2025
2a3a786
Update 10/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
186dfc1
Update 13/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
6f3719d
Update 13/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
ea25591
Update 13/umbraco-cms/reference/routing/request-pipeline/published-co…
eshanrnh Sep 4, 2025
4c1f28a
Update 13/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
c659f45
Update 13/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
c4032ec
Update 15/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
16d5674
Update 15/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
c0d92e6
Update 15/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
71cfa2a
Update 15/umbraco-cms/reference/routing/request-pipeline/published-co…
eshanrnh Sep 4, 2025
b8ae53b
Update 15/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
e82b5f5
Update 16/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
89ca8a7
Update 16/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
5374bda
Update 16/umbraco-cms/reference/routing/custom-routes.md
eshanrnh Sep 4, 2025
a55c1af
Update 16/umbraco-cms/reference/routing/request-pipeline/published-co…
eshanrnh Sep 4, 2025
f479a45
Update 16/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
16a4955
Update 10/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
ec6b60d
Update 13/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
db58d9a
Update 15/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
106d9f6
Update 16/umbraco-cms/reference/routing/surface-controllers/README.md
eshanrnh Sep 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ You can get your connection string from your Azure Portal under "Access Keys".

You're almost there. The last step is to set up the required services and middleware. This may sound daunting, but thankfully there are extension methods that do all this for you. All you need to do is invoke them in the `ConfigureServices` and `Configure` methods in the `startup.cs` file.

Invoke the `.AddAzureBlobMediaFileSystem()` extention method in the `ConfigureServices` method:
Invoke the `.AddAzureBlobMediaFileSystem()` extension method in the `ConfigureServices` method:

```C#
public void ConfigureServices(IServiceCollection services)
Expand Down
6 changes: 3 additions & 3 deletions 10/umbraco-cms/reference/routing/custom-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public IActionResult Product(string id)
}
```

This method is a bit more interesting, here we get some extra data from a different source, in this case a `DbContext`, but this can be anything you want, using the id we get from the route values. We use this extra data to create a custom model, wich includes the available stores, which we then render the view with.
Here, we get some extra data from a different source. In this case, a `DbContext`, but this can be anything you want, using the ID we get from the route values. We use this extra data to create a custom model, which includes the available stores, which we then render the view with.

It's important to note that this custom model must implement `IPublishedContent`, to do this we inherit from the `ContentModel` class, in this case our model looks like this:

Expand Down Expand Up @@ -228,7 +228,7 @@ public IPublishedContent FindContent(ActionExecutingContext actionExecutingConte
}
```

We start off by getting our product root using the `UmbracoContext` to get it based off its id. Next we need to figure out what action is being requested, to do this we cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` propperty. If the action name is index, we just return the product root, but if it's product, we try to get the SKU from the route value `id`, and try to find the child node which matches the SKU and return that.
Start by retrieving the product root using the `UmbracoContext` to obtain it based on its ID. Next, let's figure out what action is being requested. To do this, cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` property. If the action name is index, it returns the product root. If it's a product, we get the SKU from the route value `id` and find the matching child node.

Now there's only one last thing to do, we need to register our shop controller, if you're creating a controller for your own site you can do it in the `Configure` method of `Startup.cs` like so:

Expand Down Expand Up @@ -260,7 +260,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
}
```

As you can see there's nothing Umbraco specific abouth the controller routing, it's using the default `MapController` route of the `EndpointRouteBuilder`, we give our mapping a name, a pattern for the controller and some default values, so if no action is specified it will default to `Index`.
There's nothing Umbraco-specific about the controller routing; it's using the default `MapController` route of the `EndpointRouteBuilder`. Give the mapping a name, a pattern for the controller, and some default values, so if no action is specified, it will default to `Index`.

If you're creating a package you won't have access to the `Startup.cs`, so instead you can use a composer with an `UmbracoPipelineFilter` like so:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ namespace RoutingDocs.UrlProviders

The GetOtherUrls method is only used in the Umbraco Backoffice to provide a list to editors of other Urls which also map to the node.

For example, let's consider a convention-led `umbracoUrlAlias` property that enables editors to specify a comma delimited list of alternative urls for the node. It has a corresponding `AliasUrlProvider` registered in the `UrlProviderCollecton` to display this list to the Editor in the backoffice Info Content app for a node.
For example, let's consider a convention-led `umbracoUrlAlias` property that enables editors to specify a comma delimited list of alternative urls for the node. It has a corresponding `AliasUrlProvider` registered in the `UrlProviderCollection` to display this list to the Editor in the backoffice Info Content app for a node.

### Url Provider Mode

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ When finding published content the `PublishedRouter` will first check if the `Pu

You can also implement your own content finders and last chance finder, for more information, see [IContentFinder](icontentfinder.md)

The `PublishedRouter` will also follow any internal redirects there might be, it is however limited, as to not spiral out of control if there is an infite loop of redirects.
The `PublishedRouter` will also follow any internal redirects, but it is limited to avoid spiraling out of control due to an infinite redirect loop.

### Find template

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
* `~/App_Plugins/{areaname}/Views/{controllername}/`
* `~/App_Plugins/{areaname}/Views/Shared/`

Since you're only able to place static filese within your package's `App_Plugin` folder, it's highly recommend to ensure that the area you use is the same as your package name, since that allows your views to be found.
Since you're only able to place static files in your package's `App_Plugin` folder, it is highly recommended to ensure that the area you use matches your package name. This allows your views to be located easily.

Check warning on line 144 in 10/umbraco-cms/reference/routing/surface-controllers/README.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐢 [UmbracoDocs.Editorializing] Consider removing 'easily' as it can be considered opinionated. Raw Output: {"message": "[UmbracoDocs.Editorializing] Consider removing 'easily' as it can be considered opinionated.", "location": {"path": "10/umbraco-cms/reference/routing/surface-controllers/README.md", "range": {"start": {"line": 144, "column": 206}}}, "severity": "WARNING"}

Check warning on line 144 in 10/umbraco-cms/reference/routing/surface-controllers/README.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐢 [UmbracoDocs.SentenceLength] Write shorter sentences (less than 25 words). For content inside note or warning blocks, add blank lines around the content. Raw Output: {"message": "[UmbracoDocs.SentenceLength] Write shorter sentences (less than 25 words). For content inside note or warning blocks, add blank lines around the content.", "location": {"path": "10/umbraco-cms/reference/routing/surface-controllers/README.md", "range": {"start": {"line": 144, "column": 1}}}, "severity": "WARNING"}

The controller itself should not be placed in the App_Plugins folder, the App_Plugins folder is for static files only, compiled files like the controller will be included in the dlls used by the nuget package.

Expand Down
2 changes: 1 addition & 1 deletion 13/umbraco-cms/reference/routing/custom-middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The addition of the `PostRouting` callback is to allow correctly configuring the
* `WithCustomMiddleware()` is a method that can be used in Umbraco for adding custom middleware. This includes some specific customizable instructions that run in the request processing pipeline.

{% hint style="warning" %}
Using `WithCustomMiddleware()` instead of `WithMiddelware()` should only be used as a last resort. This is because Umbraco can break if you forget to add middleware or add them in the wrong order.
Using `WithCustomMiddleware()` instead of `WithMiddleware()` should only be used as a last resort. This is because Umbraco can break if you forget to add middleware or add them in the wrong order.
{% endhint %}

## Configuring the Cross-Origin Resource Sharing (CORS) middleware
Expand Down
8 changes: 4 additions & 4 deletions 13/umbraco-cms/reference/routing/custom-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public IActionResult Product(string id)
}
```

This method is a bit more interesting, here we get some extra data from a different source, in this case a `DbContext`, but this can be anything you want, using the id we get from the route values. We use this extra data to create a custom model, wich includes the available stores, which we then render the view with.
Here, we get some extra data from a different source. In this case, a `DbContext`, but this can be anything you want, using the ID we get from the route values. We use this extra data to create a custom model, which includes the available stores, which we then render the view with.

It's important to note that this custom model must implement `IPublishedContent`, to do this we inherit from the `ContentModel` class, in this case our model looks like this:

Expand Down Expand Up @@ -214,7 +214,7 @@ public IPublishedContent FindContent(ActionExecutingContext actionExecutingConte
}
```

We start off by getting our product root using the `UmbracoContext` to get it based off its id. Next we need to figure out what action is being requested. To do this we cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` propperty. If the action name is index, we return the product root. If it's product, we try to get the SKU from the route value `id`. Then we try to find the child node which matches the SKU and return that.
Start by retrieving the product root using the `UmbracoContext` to obtain it based on its ID. Next, let's figure out what action is being requested. To do this, cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` property. If the action name is index, it returns the product root. If it's a product, we get the SKU from the route value `id` and find the matching child node.

There's only one last thing to do. We need to register our shop controller. If you're creating a controller for your own site you can do it in the `Program.cs` like so:

Expand All @@ -238,7 +238,7 @@ app.UseUmbraco()
});
```

As you can see there's nothing Umbraco specific abouth the controller routing, it's using the default `MapController` route of the `EndpointRouteBuilder`, we give our mapping a name, a pattern for the controller and some default values, so if no action is specified it will default to `Index`.
There's nothing Umbraco-specific about the controller routing; it's using the default `MapController` route of the `EndpointRouteBuilder`. Give the mapping a name, a pattern for the controller, and some default values, so if no action is specified, it will default to `Index`.

If you're creating a package you won't have access to the `Program.cs`, so instead you can use a composer with an `UmbracoPipelineFilter` like so:

Expand Down Expand Up @@ -491,4 +491,4 @@ public class ShopControllerComposer : IComposer

The `Compose` method of our composer is much the same as any other normal routing. The one difference is that we call `ForUmbracoPage` on the `MapControllerRoute` where we pass in our `FindContent` method. The `FindContent` method is almost the same as it was in the controller in the `IVirtualPageController` example, with one important difference. Since we can no longer inject our required service into the constructor, we instead request them using `actionExecutingContext.HttpContext.RequestServices.GetRequiredService`. You should _not_ save the `HttpContext` or the `IServiceProvider` you get from the `actionExecutingContext` to a field or property on the class. The reason for this is that they will be specific to each request.

With this we have a custom routed controller within the Umbraco pipeline. If you navigate to `/shop` or `/shop/product/<SKU>` you will see the controllers actions being called with the content found in `FindContent`.zspo
With this we have a custom routed controller within the Umbraco pipeline. If you navigate to `/shop` or `/shop/product/<SKU>` you will see the controllers actions being called with the content found in `FindContent`.
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ If you want to have multiple URL providers, you can add them one after the other

The GetOtherUrls method is only used in the Umbraco Backoffice to provide a list to editors of other Urls which also map to the node.

For example, let's consider a convention-led `umbracoUrlAlias` property that enables editors to specify a comma-delimited list of alternative URLs for the node. It has a corresponding `AliasUrlProvider` registered in the `UrlProviderCollecton` to display this list to the Editor in the backoffice Info Content app for a node.
For example, let's consider a convention-led `umbracoUrlAlias` property that enables editors to specify a comma-delimited list of alternative URLs for the node. It has a corresponding `AliasUrlProvider` registered in the `UrlProviderCollection` to display this list to the Editor in the backoffice Info Content app for a node.

### Url Provider Mode

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ When finding published content the `PublishedRouter` will first check if the `Pu

You can also implement your own content finders and last chance finder, for more information, see [IContentFinder](icontentfinder.md)

The `PublishedRouter` will also follow any internal redirects there might be, it is however limited, as to not spiral out of control if there is an infite loop of redirects.
The `PublishedRouter` will also follow any internal redirects, but it is limited to avoid spiraling out of control due to an infinite redirect loop.

### Find template

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
* `~/App_Plugins/{areaname}/Views/{controllername}/`
* `~/App_Plugins/{areaname}/Views/Shared/`

Since you're only able to place static filese within your package's `App_Plugin` folder, it's highly recommend to ensure that the area you use is the same as your package name, since that allows your views to be found.
Since you're only able to place static files in your package's `App_Plugin` folder, it is highly recommended to ensure that the area you use matches your package name. This allows your views to be located easily.

Check warning on line 139 in 13/umbraco-cms/reference/routing/surface-controllers/README.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐢 [UmbracoDocs.Editorializing] Consider removing 'easily' as it can be considered opinionated. Raw Output: {"message": "[UmbracoDocs.Editorializing] Consider removing 'easily' as it can be considered opinionated.", "location": {"path": "13/umbraco-cms/reference/routing/surface-controllers/README.md", "range": {"start": {"line": 139, "column": 206}}}, "severity": "WARNING"}

Check warning on line 139 in 13/umbraco-cms/reference/routing/surface-controllers/README.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐢 [UmbracoDocs.SentenceLength] Write shorter sentences (less than 25 words). For content inside note or warning blocks, add blank lines around the content. Raw Output: {"message": "[UmbracoDocs.SentenceLength] Write shorter sentences (less than 25 words). For content inside note or warning blocks, add blank lines around the content.", "location": {"path": "13/umbraco-cms/reference/routing/surface-controllers/README.md", "range": {"start": {"line": 139, "column": 1}}}, "severity": "WARNING"}

The controller itself should not be placed in the App_Plugins folder, the App_Plugins folder is for static files only, compiled files like the controller will be included in the dlls used by the nuget package.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ When you are implementing your own custom authentication on Users and/or Members
The process requires adding a couple of new classes (`.cs` files) to your Umbraco project:

* **Custom-named configuration** to add additional configuration for handling different options related to the authentication. [See a generic example of the configuration class to learn more.](#custom-named-configuration)
* A **static extention class** to extend on the default authentication implementation in Umbraco CMS for either Users or Members. [See a generic example of the static extension class to learn more.](#static-extension-class)
* A **static extension class** to extend on the default authentication implementation in Umbraco CMS for either Users or Members. [See a generic example of the static extension class to learn more.](#static-extension-class)

To register these two classes in Umbraco CMS you need to add them to the `Program.cs` file.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ At this point, the 2FA is active, but no members have set up 2FA yet. The setup

You can also check that the **Two-factor Authentication** option is checked on the member in the Umbraco backoffice.

![Check the Member profile in the Umbraco backoffice to verify whether two-factor authentication is enabeld.](images/2fa-member-backoffice.png)
![Check the Member profile in the Umbraco backoffice to verify whether two-factor authentication is enabled.](images/2fa-member-backoffice.png)

### Notification when 2FA is requested for a member

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ To leverage the `WebhookEventContentBase<TNotification, TEntity>` class, follow

- **ConvertEntityToRequestPayload**: Implement this method to customize the content entity payload before sending it to webhooks.

If we take a look at the `ContentPublishedWebhookEvent`, we can see how these methods are overriden.
If we take a look at the `ContentPublishedWebhookEvent`, we can see how these methods are overridden.

```csharp
protected override IEnumerable<IContent> GetEntitiesFromNotification(ContentPublishedNotification notification) => notification.PublishedEntities;
Expand Down
2 changes: 1 addition & 1 deletion 15/umbraco-cms/reference/routing/custom-middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The addition of the `PostRouting` callback is to allow correctly configuring the
* `WithCustomMiddleware()` is a method that can be used in Umbraco for adding custom middleware. This includes some specific customizable instructions that run in the request processing pipeline.

{% hint style="warning" %}
Using `WithCustomMiddleware()` instead of `WithMiddelware()` should only be used as a last resort. This is because Umbraco can break if you forget to add middleware or add them in the wrong order.
Using `WithCustomMiddleware()` instead of `WithMiddleware()` should only be used as a last resort. This is because Umbraco can break if you forget to add middleware or add them in the wrong order.
{% endhint %}

## Configuring the Cross-Origin Resource Sharing (CORS) middleware
Expand Down
8 changes: 4 additions & 4 deletions 15/umbraco-cms/reference/routing/custom-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public IActionResult Product(string id)
}
```

This method is a bit more interesting, here we get some extra data from a different source, in this case a `DbContext`, but this can be anything you want, using the id we get from the route values. We use this extra data to create a custom model, wich includes the available stores, which we then render the view with.
Here, we get some extra data from a different source. In this case, a `DbContext`, but this can be anything you want, using the ID we get from the route values. We use this extra data to create a custom model, which includes the available stores, which we then render the view with.

It's important to note that this custom model must implement `IPublishedContent`, to do this we inherit from the `ContentModel` class, in this case our model looks like this:

Expand Down Expand Up @@ -213,7 +213,7 @@ public IPublishedContent FindContent(ActionExecutingContext actionExecutingConte
}
```

We start off by getting our product root using the `UmbracoContext` to get it based off its id. Next we need to figure out what action is being requested. To do this we cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` propperty. If the action name is index, we return the product root. If it's product, we try to get the SKU from the route value `id`. Then we try to find the child node which matches the SKU and return that.
Start by retrieving the product root using the `UmbracoContext` to obtain it based on its ID. Next, let's figure out what action is being requested. To do this, cast the `actionExecutingContext.ActionDescriptor` to a `ControllerActionDescriptor` and use its `ActionName` property. If the action name is index, it returns the product root. If it's a product, we get the SKU from the route value `id` and find the matching child node.

There's only one last thing to do. We need to register our shop controller. If you're creating a controller for your own site you can do it in the `Program.cs` like so:

Expand All @@ -236,7 +236,7 @@ app.UseUmbraco()
});
```

As you can see there's nothing Umbraco specific abouth the controller routing, it's using the default `MapController` route of the `EndpointRouteBuilder`, we give our mapping a name, a pattern for the controller and some default values, so if no action is specified it will default to `Index`.
There's nothing Umbraco-specific about the controller routing; it's using the default `MapController` route of the `EndpointRouteBuilder`. Give the mapping a name, a pattern for the controller, and some default values, so if no action is specified, it will default to `Index`.

If you're creating a package you won't have access to the `Program.cs`, so instead you can use a composer with an `UmbracoPipelineFilter` like so:

Expand Down Expand Up @@ -488,4 +488,4 @@ public class ShopControllerComposer : IComposer

The `Compose` method of our composer is much the same as any other normal routing. The one difference is that we call `ForUmbracoPage` on the `MapControllerRoute` where we pass in our `FindContent` method. The `FindContent` method is almost the same as it was in the controller in the `IVirtualPageController` example, with one important difference. Since we can no longer inject our required service into the constructor, we instead request them using `actionExecutingContext.HttpContext.RequestServices.GetRequiredService`. You should _not_ save the `HttpContext` or the `IServiceProvider` you get from the `actionExecutingContext` to a field or property on the class. The reason for this is that they will be specific to each request.

With this we have a custom routed controller within the Umbraco pipeline. If you navigate to `/shop` or `/shop/product/<SKU>` you will see the controllers actions being called with the content found in `FindContent`.zspo
With this we have a custom routed controller within the Umbraco pipeline. If you navigate to `/shop` or `/shop/product/<SKU>` you will see the controllers actions being called with the content found in `FindContent`.
Loading
Loading