MVC areas not diplaying when registering routes and areas with MVC extensions - asp.net

I am using MVC Extension with Autofac and I am having issues with my areas. I'm not sure what I am doing wrong.
Initially I had the following in my global.asax.cs:
public class MvcApplication : AutofacMvcApplication
{
public MvcApplication()
{
Bootstrapper.BootstrapperTasks
.Include<RegisterControllers>();
}
}
protected override void OnStart()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
base.OnStart();
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
With this code my areas displayed well. http://localhost:19857/Administration displays my Index view.
If I want MVC Extensions to register my routes and areas for me then http://localhost:19857/Administration displays nothing, just a 404 error.
This is the updated global.asax.cs to register my routes and areas:
public class MvcApplication : AutofacMvcApplication
{
public MvcApplication()
{
Bootstrapper.BootstrapperTasks
.Include<RegisterAreas>()
.Include<RegisterControllers>()
.Include<RegisterRoutesBootstrapperTask>();
}
protected override void OnStart()
{
RegisterGlobalFilters(GlobalFilters.Filters);
base.OnStart();
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
My RegisterRoutesBootstrapperTask class:
public class RegisterRoutesBootstrapperTask : RegisterRoutesBase
{
public RegisterRoutesBootstrapperTask(RouteCollection routes)
: base(routes)
{
}
protected override void Register()
{
Routes.Clear();
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
}
Why won't my areas display?
UPDATE
When I go to http://localhost:19857/Administration then defaults to the Dashboard controllers Index view. Here is my area's registration code:
public override string AreaName
{
get
{
return "Administration";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Administration_default",
"Administration/{controller}/{action}/{id}",
new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
}

I seem to have sorted out the problem. The problem is with the Routes.Clear(); I took it out and now everything is working fine. Here is my changes that I did to the code above:
public class RegisterRoutesBootstrapperTask : RegisterRoutesBase
{
public RegisterRoutesBootstrapperTask(RouteCollection routes)
: base(routes)
{
}
protected override void Register()
{
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Here is my updated global.asax.cs:
public class MvcApplication : AutofacMvcApplication
{
public MvcApplication()
{
Bootstrapper.BootstrapperTasks
.Include<RegisterAreas>()
.Include<RegisterControllers>()
.Include<RegisterRoutesBootstrapperTask>()
.Include<AutoMapperBootstrapperTask>();
}
}

Related

Swagger UI not showing controllers/routes for ASP.NET API

I'm trying to setup Swagger for my API, I have the interface at http://localhost/myAPI/swagger, but my controllers/routes are not displayed.
I'm using .net-framework, not .net-core
Startup:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
Route config :
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional }
);
}
}
Controller:
[RoutePrefix("v1/controller")]
public class TestController : ApiController
{
[Route("client")]
[HttpPut]
public HttpResponseMessage CreateClient([FromUri] string id)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
[Route("portfolio")]
[HttpPost]
public IResponseItem<int> CreatePortfolio([FromUri] string id)
{
return new ResponseItem<int>
{
StatusCode = HttpStatusCode.Created,
Message = "Portfolio successfully created",
Item = 12
};
}
}
Swagger config :
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebAPI");
})
.EnableSwaggerUi(c =>
{
});
}
}
I'm new to this so I'm probably missing something
first of all, it seems you made a mistake in RoutePrefix section. If you mean by defining v1/controller to use the name of your controller dynamically in the path, you should put [] around it like this:
[RoutePrefix("v1/[Controller]")]
and about the registration. you need to remove {} from your code.your code should be like the following:
GlobalConfiguration.Configuration
.EnableSwagger(c =>c.SingleApiVersion("v1", "WebAPI"))
.EnableSwaggerUi();
I recommend to register swagger directly into the Application_Start() section.
Your final code should be like this :
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configuration
.EnableSwagger(c => c.SingleApiVersion("v1", "title of your api"))
.EnableSwaggerUi();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
and the address to refer is: http://localhost:yourProgramPort/swagger/docs/v1
I hope it is helpful.

Url.Action() does not use good route config

I have overridden a default route in my routes configuration :
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("MyController_OverriddenAction",
"MyController/OverriddenAction",
new { controller = "MyOverriddenController", action = "OverridenAction" },
new[] { "Plugin" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "home", action = "Index", id = UrlParameter.Optional }
);
}
}
If I call MyController/OverriddenAction, the overridden action OverriddenAction in MyOverriddenController is displayed. It works.
But if I call #Html.Action("OverriddenAction", "MyController"), the default route is called.
Why? What is the solution?
My controllers :
public class MyOverridenController : Controller
{
public ActionResult OverriddenAction()
{
return Content("overridden");
}
}
public class MyController : Controller
{
public ActionResult OverriddenAction()
{
return new EmptyResult();
}
[...]
}
You should use following syntax
#Html.Action("OverriddenAction", "MyOverriden")
while calling the method directly like MyController/OverriddenAction it looks for the entry in MapRoute. However, while using #Html.Action you should use the actual controller name.

Language-specific Default URL using MVC's Attribute Routing and RouteLocalization.mvc

I would like to be able to create a succinct language-specific default URL for my website so that when someone browses to:
somesite.com
They get redirected to a language-culture page such as:
somesite.com/en-US/
somesite.com/sp-MX/
somesite.com/fr-FR/
Specifically, I do not want /Home/Index appended to the URLs:
somesite.com/en-US/Home/Index
somesite.com/sp-MX/Home/Index
somesite.com/fr-FR/Home/Index
I am committed to making this site using RouteLocalization.mvc
Dresel/RouteLocalization
Translating Your ASP.NET MVC Routes
And I would like to use MVC Attribute Routing to the extent feasible.
I am having trouble figuring out how to cause the Start() method redirect to a language-culture specific URL without the addition of something like "index".
Samples of what I have attempted follow:
using RouteLocalization.Mvc;
using RouteLocalization.Mvc.Extensions;
using RouteLocalization.Mvc.Setup;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.Clear();
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);
const string en = "en-us";
ISet<string> acceptedCultures = new HashSet<string>() { en, "de", "fr", "es", "it" };
routes.Localization(configuration =>
{
configuration.DefaultCulture = en;
configuration.AcceptedCultures = acceptedCultures;
configuration.AttributeRouteProcessing = AttributeRouteProcessing.AddAsNeutralAndDefaultCultureRoute;
configuration.AddCultureAsRoutePrefix = true;
configuration.AddTranslationToSimiliarUrls = true;
}).TranslateInitialAttributeRoutes().Translate(localization =>
{
localization.AddRoutesTranslation();
});
CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = Localization.DetectCultureFromBrowserUserLanguages(acceptedCultures, en);
var defaultCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
routes.MapRoute(
name: "DefaultLocalized",
url: "{culture}/{controller}/{action}/{id}",
constraints: new { culture = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { culture = defaultCulture, controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
And my home controller:
public class HomeController : Controller
{
[HttpGet]
[Route]
public RedirectToRouteResult Start()
{
return RedirectToAction("Home", new { culture = Thread.CurrentThread.CurrentCulture.Name });
}
[Route("Index", Name = "Home.Index")]
public ActionResult Index()
{
return View();
}
public ActionResult Contact()
{
return View();
}
public ActionResult About()
{
return View();
}
}
My Global.asax file:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
}
}
Redirecting is a separate concern than routing. Since your goal of redirecting any URL to its localized counterpart is a cross-cutting concern your best bet is to make a global filter.
public class RedirectToUserLanguageFilter : IActionFilter
{
private readonly string defaultCulture;
private readonly IEnumerable<string> supportedCultures;
public RedirectToUserLanguageFilter(string defaultCulture, IEnumerable<string> supportedCultures)
{
if (string.IsNullOrEmpty(defaultCulture))
throw new ArgumentNullException("defaultCulture");
if (supportedCultures == null || !supportedCultures.Any())
throw new ArgumentNullException("supportedCultures");
this.defaultCulture = defaultCulture;
this.supportedCultures = supportedCultures;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var routeValues = filterContext.RequestContext.RouteData.Values;
// If there is no value for culture, redirect
if (routeValues != null && !routeValues.ContainsKey("culture"))
{
string culture = this.defaultCulture;
var userLanguages = filterContext.HttpContext.Request.UserLanguages;
if (userLanguages.Length > 0)
{
foreach (string language in userLanguages.SelectMany(x => x.Split(';')))
{
// Check whether language is supported before setting it.
if (supportedCultures.Contains(language))
{
culture = language;
break;
}
}
}
// Add the culture to the route values
routeValues.Add("culture", culture);
filterContext.Result = new RedirectToRouteResult(routeValues);
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Do nothing
}
}
Usage
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RedirectToUserLanguageFilter("en", new string[] { "en", "de", "fr", "es", "it" }));
filters.Add(new HandleErrorAttribute());
}
}
Note also that your routing is misconfigured. The route setup is run one time per application startup, so setting the default culture to that of the current thread is meaningless. In fact, you should not be setting a default culture at all for your culture route because you want it to miss so your Default route will execute if there is no culture set.
routes.MapRoute(
name: "DefaultLocalized",
url: "{culture}/{controller}/{action}/{id}",
constraints: new { culture = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

404 on Login and LogOff methods after setting up area routing

I have an MVC5 application using ASP Identity 2 and Fluentsecurity with the following methods in the Home controller which handle the log in page and logging out:
public ActionResult Login(string returnUrl)
{
this.ViewBag.ReturnUrl = returnUrl;
return this.View();
}
public ActionResult LogOff()
{
this.AuthenticationManager.SignOut();
return this.RedirectToAction("Login", "Home");
}
This works absolutely fine.
I now need to add an area into my application and set up routing accordingly. I have added a sub folder called 'Admin' under the 'Areas' folder and placed the relevant controllers and views in there in the appropriate subfolders.
I have then set up the routing as follows in Global.asax:
private static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AppName.Web.Controllers " });
routes.MapRoute(
"Admin",
"Admin/{controller}/{action}/{querydata}",
new { querydata = UrlParameter.Optional },
new[] { "AppName.Web.Areas.Admin.Controllers" });
}
Which is then called as below:
private void Application_Start(object sender, EventArgs e)
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
SecurityConfigurator.Configure(SecurityConfig.Configure);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
FilterConfig.RegisterGlobalApiFilters(GlobalConfiguration.Configuration.Filters);
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.DefaultNamespaces.Add("AppName.Web.Areas.Admin.Controllers");
}
If i then run the application, routing is working as expected in my area and the main application, apart from it seems to have broken the Login and LogOff methods in the aforementioned Home controller, now giving a 404 error when trying to access them. The index method of the same Home controller works fine though.
In case it's related to Fluentsecurity, this is how security is configured:
internal static void Configure(ConfigurationExpression configuration)
{
configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(GetRoles);
configuration.For<HomeController>(x => x.Login(default(string))).DenyAuthenticatedAccess();
configuration.For<HomeController>(x => x.Index()).DenyAnonymousAccess();
configuration.For<HomeController>(x => x.LogOff()).AllowAny();
//Admin Home
configuration.For<Areas.Admin.Controllers.HomeController>(x => x.Index()).DenyAnonymousAccess();
}
Any ideas where I may be going wrong?
private static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Admin",
"Admin/{controller}/{action}/{querydata}",
new { querydata = UrlParameter.Optional },
new[] { "AppName.Web.Areas.Admin.Controllers" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AppName.Web.Controllers " });
}

Unable to reach ASP.NET MVC Web Api endpoint

I am working on an ASP.NET MVC app. I am trying to create a basic API. I created my first Web API controller by right-clicking on Controllers, Add -> Controller... then choosing "Web API 2 Controller - Empty". In the controller code, I have the following:
namespace MyProject.Controllers
{
public class MyApiController : ApiController
{
public IHttpActionResult Get()
{
var results = new[]
{
new { ResultId = 1, ResultName = "Bill" },
new { ResultId = 2, ResultName = "Ted" }
};
return Ok(results);
}
}
}
When I run the app, I enter http://localhost:61549/api/myApi in the browser's address bar. Unfortunately, I get a 404. I'm just trying to create an API endpoint that returns a hard-coded set of JSON objects. I need this to test some client-side JavaScript. What am I doing wrong?
Here are how my routes are registered:
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Make sure that you have the WebApiConfig registration being called, possibly in the Global.asax Application_Start() method. Something like:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
You did not add method name at the end of call. Try this one:
http://localhost:61549/api/myapi/get
Try this approach
namespace MyProject.Controllers
{
public class MyApiController : ApiController
{
public IHttpActionResult Get()
{
var results = new List<ResultModel>
{
new ResultModel() {ResultId = 1, ResultName = "Bill"},
new ResultModel() {ResultId = 2, ResultName = "Ted"}
};
return Ok(results);
}
}
public class ResultModel
{
public int ResultId { get; set; }
public string ResultName { get; set; }
}
}
Api: http://localhost:61549/api/MyApi/get
Hope this helps.

Resources