c# - route - mvc routing examples
¿Por qué recibo la excepción "No se encontró un método de acción pública en el controlador"? (3)
Estoy utilizando el siguiente artículo Adición a la localización ASP.NET MVC: uso del enrutamiento para admitir rutas multiculturales.
Si mira la sección "Registro de rutas", verá que las rutas actuales están actualizadas (en el método "RegisterRoutes") con el segmento "{culture}".
La diferencia es que quiero mantener las rutas actuales y agregar un duplicado para cada una con un segmento "{culture}", por lo que para una ruta como "foo / bar" obtendría un duplicado de "{culture} / foo / bar ".
Puedes ver que también me estoy asegurando de que la nueva ruta sea lo primero.
public static void MapMvcMultiCultureAttributes(this RouteCollection routes, bool inheritedRoutes = true, string defaultCulture = "en-US", string cultureCookieName = "culture")
{
routes.MapMvcAttributeRoutes(inheritedRoutes ? new InheritedRoutesProvider() : null);
var multiCultureRouteHandler = new MultiCultureMvcRouteHandler(defaultCulture, cultureCookieName);
var initialList = routes.ToList();
routes.Clear();
foreach (var routeBase in initialList)
{
var route = routeBase as Route;
if (route != null)
{
if (route.Url.StartsWith("{culture}"))
{
continue;
}
var cultureUrl = "{culture}";
if (!String.IsNullOrWhiteSpace(route.Url))
{
cultureUrl += "/" + route.Url;
}
var cultureRoute = routes.MapRoute(null, cultureUrl, null, new
{
culture = "^//D{2,3}(-//D{2,3})?$"
});
cultureRoute.Defaults = route.Defaults;
cultureRoute.DataTokens = route.DataTokens;
foreach (var constraint in route.Constraints)
{
cultureRoute.Constraints.Add(constraint.Key, constraint.Value);
}
cultureRoute.RouteHandler = multiCultureRouteHandler;
route.RouteHandler = multiCultureRouteHandler;
}
routes.Add(routeBase);
}
}
El "proveedor de Herederos Heredados" se ve así:
private class InheritedRoutesProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(ActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes(typeof(IDirectRouteFactory), true)
.Cast<IDirectRouteFactory>()
.ToArray();
}
}
Mi controlador se ve así:
public class MyBaseController: Controller
{
[HttpGet]
[Route("bar")]
public virtual ActionResult MyAction(){
{
return Content("Hello stranger!");
}
}
[RoutePrefix("foo")]
public class MyController: MyBaseController
{
}
Mi método "RegisterRoutes" se ve así:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcMultiCultureAttributes();
routes.LowercaseUrls = true;
}
Ahora, si lo hago:
- / foo / bar - ¡TRABAJOS!
- / es- ES / foo / bar - HttpException No se encontró un método de acción pública ''MyAction'' en el controlador ''MyController''
Puedo darte un ejemplo de cómo lo haría. Ese ejemplo que estás usando es bastante viejo.
Implemente en sus controladores (use inheritance ) el método BeginExecuteCore de la siguiente manera:
protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) { string cultureName = RouteData.Values["culture"] as string; if (cultureName == null) cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : // obtain it from HTTP header AcceptLanguages null; // Validate culture name cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe if (RouteData.Values["culture"] as string != cultureName) { // Force a valid culture in the URL RouteData.Values["culture"] = cultureName.ToLower(); // lower case too // Redirect user Response.RedirectToRoute(RouteData.Values); } // Modify current thread''s cultures Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; return base.BeginExecuteCore(callback, state); }
Agregue algunas rutas
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Custom", url: "{controller}/{action}/{culture}", defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Coordinate", action = "Index" }
Implementar una clase de ayuda cultural
clase estática pública CultureHelper {private static readonly List _cultures = new List {"listOfClutures"};
public static bool IsRighToLeft()
{
return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.IsRightToLeft;
}
public static string GetImplementedCulture(string name)
{
if (string.IsNullOrEmpty(name))
return GetDefaultCulture(); // return Default culture
if (!CultureInfo.GetCultures(CultureTypes.SpecificCultures).Any(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)))
return GetDefaultCulture(); // return Default culture if it is invalid
if (_cultures.Any(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)))
return name; // accept it
var n = GetNeutralCulture(name);
foreach (var c in _cultures)
if (c.StartsWith(n))
return c;
return GetDefaultCulture(); // return Default culture as no match found
}
public static string GetDefaultCulture()
{
return "en-GB"; // return Default culture, en-GB
}
public static string GetCurrentCulture()
{
return Thread.CurrentThread.CurrentCulture.Name;
}
public static string GetCurrentNeutralCulture()
{
return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name);
}
public static string GetNeutralCulture(string name)
{
if (!name.Contains("-")) return name;
return name.Split(''-'')[0]; // Read first part only. E.g. "en", "es"
}
public static List<KeyValuePair<string, string>> GetImplementedLanguageNames()
{
List<KeyValuePair<string, string>> languageNames = new List<KeyValuePair<string, string>>();
foreach (string culture in _cultures)
{
languageNames.Add(new KeyValuePair<string, string>(culture, CultureInfo.GetCultureInfo(culture).EnglishName));
}
languageNames.Sort((firstPair, nextPair) =>
{
return firstPair.Value.CompareTo(nextPair.Value);
});
string currCulture = GetCurrentCulture();
languageNames.Remove(new KeyValuePair<string, string>(currCulture, CultureInfo.GetCultureInfo(currCulture).EnglishName));
languageNames.Insert(0, new KeyValuePair<string, string>(currCulture, CultureInfo.GetCultureInfo(currCulture).EnglishName));
return languageNames;
}
public static string GetDateTimeUsingCurrentCulture(DateTime dateToConvert)
{
CultureInfo ci = new CultureInfo(GetCurrentCulture());
return dateToConvert.ToString(ci.DateTimeFormat.ShortDatePattern + '' '' + ci.DateTimeFormat.ShortTimePattern);
}
}
No creo que pueda lograr esto con el enrutamiento de atributos porque el RouteCollection pasado a RegisterRoutes y su RouteData posterior es bastante diferente y usa clases internas a las que no puede acceder.
Llegué a determinar que RouteData espera una clave llamada "MS_DirectRouteMatches", cuyo valor es una colección de RouteData. Google MS_DirectRouteMatches si quieres profundizar más.
En última instancia, supongo que esto no pretende ser un punto de extensión.
Obtendrá más éxito si se atiene al enrutamiento convencional. Encontré que tu código funcionaba bastante bien en este escenario, pero supongo que ya lo sabías. Lo siento, no pude darle una mejor solución.
Por lo que puedo ver, no usa el padre ActionResult. No estoy seguro de por qué sucede esto. Podría anular el resultado de acción en la clase derivada, pero a mí me parece que hay algo raro en la herencia ... un error en la forma en que administra sus clases.
public class MyBaseController: Controller
{
[HttpGet]
[Route("bar")]
public virtual ActionResult MyAction(){
{
return Content("Hello stranger!");
}
}
Asumiendo que esto no funcionará:
[RoutePrefix("foo")]
public class MyController: MyBaseController
{
[HttpGet]
[Route("foo")]
public override ActionResult MyAction(){
{
return Content("Hello stranger!");
}
}
Sugeriría hacer esto:
[RoutePrefix("foo")]
public class MyController: MyBaseController
{
[HttpGet]
[Route("foo")]
public ActionResult MyAction(){
{
return Content("Hello stranger!");
}
}
Esto al menos le dirá si la herencia es defectuosa y puede revisar su código.
Otra forma de evitar esto sería usar un controlador con dos métodos para los diferentes formatos.