
Hace algunas semanas vimos cómo crear inline route constraints, o restricciones de ruta en línea en ASP.NET Core, y creamos un ejemplo simple que permitía al sistema de routing detectar si el valor suministrado a través de un parámetro de ruta era una palabra palíndroma.
Para ello, creamos la restricción "palindrome" que, implementada en la clase PalindromeConstraint
podíamos usar de la siguiente forma:
// Uso en minimal API:
app.MapGet("/test/{str:palindrome}", (string str) => $"{str} is palindrome");
// Uso en MVC:
public class TestController : Controller
{
[HttpGet("/test/{str}")]
public string Text(string str) => $"{str} is palindrome";
}
Sin embargo, si atendemos a la lista de restricciones disponibles de serie en ASP.NET Core, vemos que hay algunas de ellas que son parametrizadas, como maxlength
o range
:
Plantilla de ruta | Significado |
---|---|
/order/{orderId:minlength(5) |
orderId debe tener como mínimo 5 caracteres |
/setAge/{age:int:range(0,120) |
age debe ser un entero entre 0 y 120 |
En este post vamos a ver precisamente eso, cómo crear una restricción personalizada con parámetros.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcore, routing, trucos

El desconocido (al menos para mí) código de estado HTTP 506 (Variant also negotiates) indica que el servidor tiene un error de configuración en el contexto de la Negociación Transparente de Resultados (RFC 2295). Permite informar al cliente de que la variante que ha elegido está configurada para participar en la negociación de contenidos, pero no es válida como endpoint para el proceso de negociación-
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo solucionar el error "Unable to connect to web server 'IIS Express'" en Visual Studio
José María Aguilar - Cómo saber si tenemos el debug=true en el Web.config
José María Aguilar
.NET Core / .NET
- .NET 7 Adds Generic Math
Jonathan Allen - Creating Containers in .NET 7 with the .NET CLI
Anuraj Parameswaran - How to get allocations in .NET? And how big is an empty array?
Steven Giesel - Static Abstract Interface Members in C#11
Dirk Strauss - HashSet vs SortedSet in C#
Code Maze - Creating JSON Web Tokens using dotnet user-jwts tool
Anuraj Parameswaran - Tagged Strings in Visual Studio and .NET 7
Jonathan Allen - Killing all child processes when the parent exits (Job Object)
Gérald Barré - Wolverine | Next Generation .NET Command and Message Bus
Jeremy D. Miller - C# List Pattern Examples
Phil Haack - How to Check if a String Ends With a Number in C#
Code Maze - The dangers and gotchas of using scoped services in OptionsBuilder
Andrew Lock - .NET 7 Removes Its Insecure XmlSecureResolver
Jonathan Allen - "Use always a StringBuilder"
Steven Giesel - Towards a Beginner-Friendly DotNet
Matt Eland - How to Create and Publish a NuGet Package with dotnet CLI
Anuraj Parameswaran - Logging to Amazon Cloudwatch with Serilog in .NET
Mukesh Murugan - Frozen collections in .NET 8
Steven Giesel - The new .NET 7.0 IParsable<TSelf> interface
Patrick Smacchia - A Mostly Complete Guide to C# 11’s Final Features
Matthew MacDonald - Sending An Email In C#/.NET
Daniel Collingwood

Hoy va un post cortito, pero que puede venir bien a alguien que esté intentando "trocear" su aplicación en distintos proyectos y se haya encontrado con este problema.
Echemos un vistazo al contenido típico del archivo App.razor
de una aplicación Blazor (Server o WebAssembly, da lo mismo):
<Router AppAssembly="@typeof(Program).Assembly">
...
</Router>
Resulta que el componente Router
escanea durante su inicialización el ensamblado que hayamos indicado en su atributo AppAssembly
, por defecto el ensamblado actual, en busca de componentes que:
- Hayan definido sus rutas mediante directivas
@page
de Razor, - o bien, que tengan su ruta especificada con atributos
[Route]
En cualquiera de los casos, los componentes localizados son añadidos a la tabla de rutas y, por tanto, será posible navegar hacia ellos modificando la ruta del navegador, ya sea programáticamente (usando el servicio NavigationManager
) o bien mediante la pulsación de enlaces o modificación directa en la barra de direcciones.
Hasta aquí, todo correcto. El problema viene cuando las páginas que queremos añadir a la tabla de rutas no están en el ensamblado actual, o se encuentran repartidas en varios ensamblados distintos, por ejemplo en:
- Otros proyectos de la solución
- Ensamblados externos, referenciados directamente
- Paquetes NuGet instalados en el proyecto

Creo que nunca me he topado en persona con este código de error, así que es una buena ocasión para conocer un poco sobre él.
El código HTTP 505 (HTTP Version Not Supported) es retornado por un servidor cuando rechaza o no soporta la versión major de HTTP utilizada en la petición. En este caso, se recomienda que el servidor incluya en la respuesta la descripción del motivo por el que no soporta la versión utilizada, y qué protocolos debería usar en su lugar.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo invocar métodos estáticos C# desde Javascript con Blazor (interop 2/3)
José María Aguilar - Imágenes en cuadros de texto de formularios web
José María Aguilar
.NET Core / .NET
- Null-Coalescing ?? And Null-Coalescing Assignment ??= Operator
Code Maze - Enforcing .NET code style rules at compile time
Daniel Genezini - The Best C# 11 Feature You Don't Need
Matt Eland - My C# array, tuple, delegate declaration dilemma
Jiří Činčura - How to Return null From a Generic Method in C#
Code Maze - Introducing the C# 11 Required Keyword
Matt Eland - My favourite 'recent' LINQ improvements
Mark Heath - How to Use the “Using Static” Feature in C#
Code Maze - Book: C# for Babies
JetBrains - Using Dynamic LINQ With System.Linq.Dynamic.Core Library
Code Maze - C# 11 and .NET 7 Bring Generic Parsing
Jonathan Allen - C# 11 required members
Patrick Smacchia - Protect yourself when deserializing - System.Text.Json
Josef Ottosson - Anonymous test data with AutoFixture
Steven Giesel - Cysharp/MemoryPack: Zero encoding extreme performance binary serializer for C# and Unity.
Yoshifumi Kawai

La llegada de C#11 viene acompañada de un buen número de novedades, algunas de ellas bastante jugosas, como los raw string literals o el nuevo modificador "file" que ya hemos ido viendo por aquí.
En esta ocasión, vamos a echar el vistazo a otra de las novedades que pueden resultar de bastante utilidad en nuestro día a día: los miembros requeridos.

Como vimos hace unas semanas, el código HTTP 502 es usado por gateways, proxies o intermediarios similares para informar al cliente de que la petición al servidor real estaba retornando un error, pero, ¿qué ocurre cuando dicho servidor ni siquiera responde en un tiempo determinado? Pues para eso existe el HTTP 504 (Gateway timeout).
Y ahora, ahí van los enlaces recopilados durante la semana pasada, con muchas novedades y lanzamientos que, como de costumbre, espero que os resulten interesantes. :-)
Por si te lo perdiste...
- ¿A qué huele tu código?
José María Aguilar - Cómo invocar funciones Javascript desde Blazor (interop)
José María Aguilar
.NET Core / .NET
- .NET 7 is Available Today
Jon Douglas - Welcome to C# 11
Mads Torgersen - Announcing F# 7
Vlad Zarytovskii - Announcing NuGet 6.4 - Signed, Central, Delivered
Jon Douglas - What's new in Windows Forms in .NET 7.0
Igor Velikorossov - What's new for WPF in .NET 7
Pankaj Chaurasia - What's new in Orleans 7.0
Brady Gaster - Mutable value types are evil! Sort of...
Steven Giesel - Top 10 Dotnet Exception Anti-Patterns in C#
Matt Eland - How to Use HTML Agility Pack in C#
Diego García - Debugging tips and tools
Gérald Barré - Tasks vs Threads in C#
Code Maze - Concurrent Processing in .NET 6 with System.Threading.Channels (Bonus: Interval Trees)
Charles Chen - Source Generated RegEx in .NET 7
Steve Collins

En código que veo, incluso escrito por mí un tiempo atrás, es muy habitual encontrar comparaciones de cadenas de caracteres en las que, para asegurar que el casing no sea tenido en cuenta, se fuerza una conversión de alguno de los operandos, o incluso ambos, a mayúsculas o minúsculas.
En escenarios muy simples esto funcionará bien y no tendrá contraindicaciones especialmente graves para nuestro sistema. Veamos unos ejemplos:
// Ejemplo 1: conversión de un único operando
int Calculate(string op, int a, int b)
{
// Pasamos a minúsculas el operador para
// asegurar que encaja con la constante
if(op.ToLower()=="add")
{
return a+b;
}
else if(op.ToLower()=="sub")
{
return a-b;
}
...
}
// Ejemplo 2: conversión de ambos operandos
bool AreBrothers(User user1, User user2)
{
// Pasamos a mayúsculas ambos apellidos por
// si alguno se ha escrito usando otro casing
var areBrothers = user1.Surname.ToUpper() == user2.Surname.ToUpper();
return areBrothers;
}
Sin embargo, aunque pueda parecer despreciable, estas operaciones de transformación a mayúsculas o minúsculas tienen un coste importante, que se pone especialmente de manifiesto cuando estamos hablando de aplicaciones con mucha carga de usuarios, alojada en infraestructura muy ajustada o cuando se requiere un rendimiento extremo.

El error HTTP 503 (Service unavailable) lo retornan los servidores para indicar que no están disponibles para responder a la petición. Es habitual encontrarlo al intentar acceder a un sitio web que está en mantenimiento, temporalmente fuera de servicio, o ante un estado de sobrecarga (alta concurrencia, bajos recursos, etc.) El cliente debe interpretarlo como un error temporal, y puede volver a intentarlo de nuevo algo más adelante; para ello, a veces suele acompañarse de un encabezado Retry-After
con la fecha/hora o el número de segundos estimados en los que volverá a estar operativo.
Y ahora, ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo mostrar y ocultar elementos en Blazor
José María Aguilar - 8 curiosidades que quizás no conocías sobre los emoticonos ;-), Parte 1 y Parte 2
José María Aguilar
.NET Core / .NET
- Announcing .NET Community Toolkit v8.1.0 Preview 1
Sergio Pedri - Local functions vs lambda expressions
Steven Giesel - Handling times for an EV charger
Jon Skeet - Deserializing Json Streams using Newtonsoft.Json & System.Text.Json with C# & VB
Graeme_Grant - Advanced LINQ
Alvaro Montoya - Injectio - Source Generator for Dependency Injection
Paul Welter - Encrypt and Decrypt Text Values in .NET
Jamil Hallal - Getting Started With Worker Services
Assis Zang - Just store UTC? Not so fast! Handling Time zones is complicated.
Derek Comartin - Bring WCF apps to the latest .NET with CoreWCF and Upgrade Assistant
Sam Spencer - Marking API's as obsolete or as experimental
Steven Giesel