Attribute routing not picking up index action in routedata - asp.net-mvc

I have the following MVC Controller:
[RoutePrefix("our-brochure")]
[Route("{action=index}")]
public class OurBrochureController : Controller
{
public ActionResult Index()
{
return View();
}
}
Which works fine - if I go to http://localhost/our-brochure it goes to the index page and loads it as expected
However, for my navigation, I have a NavModel that works out the current controller and action using the following code:
var httpContext = new HttpContextWrapper(HttpContext.Current);
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(httpContext);
var controllerName = routeData.Values["controller"].ToString();
var actionName = routeData.Values["action"].ToString();
This gives me the correct controller name of "OurBrochure" but routeData.Values["action"] is null.
Is this the expected behaviour as I would think that I would get "Index" as the action in the route data. If this is the expected behaviour, how do you get the current action name?

I have solved my problem, instead of getting the RouteData using the above methods, you can get it directly from the Current Context:
HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString()

Related

MVC ViewResult.VIewName null even after setting it

I'm trying to write unit tests for a MVC application. im trying to test if my controller returns the correct view name.
This is the controller action im testing:
public IActionResult Index(string reportcode)
{
if(string.IsNullOrEmpty(reportcode))
ReportCode = reportcode;
ViewBag.GridSource = GetReportData(reportcode);
return View("Index");
}
This is my Unittest:
[Test]
public void Index_ReturnCorrectView()
{
var controller = new HomeController();
var result = controller.Index("COMD") as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
The error i get from the unit test is expected "Index" but got null.
I did a lot of search and most answers say the ViewName property should be set after you declare it when returning the view. I tried the same but it wont work still.
Thank you
The documentation for Controller.View() states:
This method overload of the View class returns a ViewResult object
that has an empty ViewName property. If you are writing unit tests for
controller actions, take into account the empty ViewName property for
unit tests that do not take a string view name.
At run time, if the ViewName property is empty, the current action
name is used in place of the ViewName property.
So when expecting a view with the same name as the current action we can just test that it's an empty string.
Alternatively, the Controller.View(ViewName, Model) method will set the ViewName.
My Controller Method
public ActionResult Index()
{
return View("Index");
}
Test Method
[TestMethod]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsTrue(result.ViewName == "Index");
}

How to specify a RedirectToRouteResult

My existing MVC code contains an action routine something like this:
[HttpPost]
public ActionResult Register1(SomeViewModel model)
{
return RedirectToAction("Register", new { p = model.PageNumber - 1 });
}
I want to move this code to a library routine:
public static ActionResult ProcessPost(Controller controller, string action,
int pageNumber)
{
// Redirect to the specified action on the specified controller
return new RedirectToRouteResult( <something here> );
}
and call it from the action routine like this:
return ProcessPost(this, "register", model.PageNumber);
Can some kind person give me the <something here> code that yields an ActionResult that redirects to the specified action (specified by the string argument) on the specified Controller (specified by the Controller argument?
Taking a look at the documentation on RedirectToRouteResult seems pretty straight forward:
var routeValues = new RouteValueDictionary();
routeValues.Add("Action", action);
routeValues.Add("Controller", controller.GetType().Name);
routeValues.Add("PageNumber", pageNumber);
var result = new (RedirectToRouteResult(routeValues);
After some experimentation, this appears to be a simple solution:
return new RedirectResult(controller.Url.RouteUrl(
new { action = action, p = pageNumber }
));
Apparently, the Url method on a specific Controller instance is smart enough to use that instance to get the controller name part of the full URL.

Why is my controller test not working, the view name is coming back empty

Doing a simple test to verify the view name for a controllers action:
var controller = new UserController();
var result = controller.Login() as ViewResult;
Assert.AreEqual("Login", result.ViewName);
The result.ViewName is coming back with "", why would this be?
Are you specifying the view name in the controller Login method or are you leaving it at the default value (which is "")?
If you leaving it at the default value, which is common, you need to test for String.Empty instead of "Login".
if you have a view like this
public ActionResult Index()
{
return View();
}
then the ViewName property will take it's default value that is "" and if you specify the view name like this it'll work fine
public ActionResult Index()
{
return View("Index");
}

How to change mvc dynamic pages route url - Why does this not work?

For the controller below, why does a call to http://localhost:port/content/about not pass "about" as the value for the page parameter of the index controller? Default routing. Clearly I do not understand routing...
public class ContentController : Controller
{
private IContentService _service;
public ContentController()
{
_service = new ContentService(new ModelStateWrapper(this.ModelState), new ContentRepository());
}
public ActionResult Index(string page)
{
return RedirectToAction("View", new { p = page });
}
public ActionResult Page(string p)
{
ContentPage contentPage = _service.GetPageContent(site, p);
return View(contentPage);
}
}
Default routing presumes you are using the following pattern: {action}/{id}. You are not using this. You need to re-declare your route so that it sends everything to the "Index" action and passes the "p" parameter.
Are you running IIS6? If so, make sure you map the asp.net isapi dll to the .mvc extension.
Routing was killing me last week until I found this out...

asp.net mvc with optional parameters and partial views

i have:
AlbumsController
PhotoRepository
Index.aspx (view)
inside of Index.aspx, i have a partial view call AlbumControl. I want to update this via ajax and ajaxhelper.
the issue is that i want the ability to do the following:
http://www.mysite.com/Albums
http://www.mysite.com/Albums?FilterTag=Birthdays
when i do this, i get the following error:
The current request for action 'Index' on controller type 'AlbumsController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Index(System.String) on type Controllers.AlbumsController
System.Web.Mvc.ActionResult Index() on type Controllers.AlbumsController
i would have thought that asp.net mvc would have figured it out where if i pass in a parameter in the querystring it would go to the Index(string Tag) method and if i didn't pass in a parameter, it would go to Index().
suggestions?
The problem is that the MVC Routing Engine can't tell the difference between:-
1) Calling Index()
and
2) Calling Index(string tag) with tag = null
That's why it says the request is ambiguous.
You can just do:-
public ActionResult Index(string tag)
{
if(String.IsNullOrEmpty(tag))
{
// index code goes here
return View("Index");
}
else
{
// code to handle filtered view goes here
return View("Tag");
}
}
or you can force the parameter to be required with a custom attribute:-
ASP.NET MVC ambiguous action methods
or you can set up routes so that Albums and Albums?FilterTag=X explicitly go to different actions (Incidentally, I'd recommend "Albums" and "Albums/X"):-
routes.MapRoute("AlbumsIndex", "Albums",
new { controller = "Albums", action = "Index" });
routes.MapRoute("AlbumsTag", "Albums/{tag}",
new { controller = "Albums", action = "Tag", tag = "" });
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" );
and
public ActionResult Index() { ... }
public ActionRestlt Tag(string tag) { ... }

Resources