ActionLink with Attribute Routing in MVC 5 - vb.net

I'm having trouble with an ActionLink in MVC 5.
#Html.ActionLink("View Commissions", "/" + item.Id.ToString, "Commissions")
#Html.ActionLink("View Commissions", "Index", "Commissions", New With {Key .payRollId = item.Id}, Nothing)
These two ActionLinks should accomplish the same thing, but I would prefer to use the second one. Unfortunately, they produce different URLs. The first creates http://mysite/Commissions/3. The second creates http://mysite/Commissions?payRollId=3.
In my Commissions controller, I have the following code:
' GET: Commissions/5
<Route("Commissions/{payRollId:min(1)}")>
Async Function Index(ByVal payRollId As Integer?) As Task(Of ActionResult)
If IsNothing(payRollId) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Return View(Await ...query...).ToListAsync)
End Function
This successfully handles the first ActionLink's URL. The second one results in a 404 error. I don't have any other RouteAttributes or mapped routes for Commissions. According to this attribute routing article, the second ActionLink should create the pretty URL (no query string) that successfully handles the request.
What am I missing? How can I get the second ActionLink to generate the proper URL (Commissions/3) to match the RouteAttribute?

Edit
This should produced the desired route:
View Commissions
This assumes you've enabled attribute based routing something like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}

I have a partial solution. I played with the controller code and found that changing the RouteAttribute to <Route("Commissions/{payRollId:min(1)?}")> (note the ? at the end) allows it to handle the second URL.
I am still working on how to generate a pretty URL using the second ActionLink. I will update this answer if I work it out.

Related

ASP.NET MVC: First parameter in action method is always empty when form is submitted

I am having a problem when passing values to Controller action method.
I have a route map like this:
routes.MapRoute(
name: "VehicleAdvancedSearchResult",
url: "search-result/{searchTypeSlug}/{condition}/{makeSlug}/{modelName}/{modelExt}/{categorySlug}/{parishSlug}/{yearRange}/{priceRange}",
defaults: new
{
controller = "SearchResult",
action = "VehicleAdvanceSearch",
//SearchTypeSlug = UrlParameter.Optional
//Condition = UrlParameter.Optional,
//MakeSlug = UrlParameter.Optional,
//ModelName = UrlParameter.Optional,
//ModelExt = UrlParameter.Optional,
//CategorySlug = UrlParameter.Optional,
//ParishSlug = UrlParameter.Optional,
//YearRange = UrlParameter.Optional,
//PriceRange = UrlParameter.Optional
}
);
I tried with UrlParameter.Optional but it did not work.
In the View I have a form with no action defined by default but when I hit submit the action attribute is added to it with correct values.
The problem is the first parameter value is always coming as empty string when the controller method is actually called.
What I am doing wrong?
The other issue is I want the action method to be called even if the URL is partially complete or even it has no parameter. In my case if I type in just http://locahots:55904/search-result/ or http://locahots:55904/search-result/for-sale/, meaning as long as it is partial, I am always ending up with a 404. But I want them to redirect to another page if it is a partial URL.
I have already done that check in my action method but the method is not at all being called with a partial URL.
This is required because Google crawls the URL with every single folder structure separately to index them.
How can I prevent this from happening?
For controller method if you want to partially call you need to defined optional parameters in method
For example,
Public ActionResult <method name>(string param1="",string param2=""){}
Now if you want to call method with partial arguments you need to pass argument right to left.
Or an alternative way is you can override method with different arguments option you have.
For this action method to be called by partial url, you can create a new route in RouteConfig file having different route name pointing to the same action method.
routes.MapRoute(
name: "VehicleAdvancedSearchResult_V2",
url: "search-result/{parameterName?}",
defaults: new
{
controller = "SearchResult",
action = "VehicleAdvanceSearch",
}
);
Second way is that you can apply Attribute Routing on this action using the Route Attribute.
For Example:
[Route("[action]/{parameterName?}")] //?--> for optional parameter
public ActionResult VehicleAdvanceSearch()
In Order to resolve the issue of First Parameter null, you can create a Model for all the parameters and then hit the controller action with Modal as parameter.
For Example:
[HttpPost]
public ActionResult VehicleAdvanceSearch(SearchResultModel model){}

ASP. NET Web Api 2 issue - The requested resource does not support HTTP method 'GET'

I am not sure why I am getting a "404 Not Found" on the following GET call to my api (using PostMan)
http://localhost:53840/api/v1/MessageLog/SomeStuff/3
The method in the Controller is as follows
[System.Web.Http.HttpGet]
public string SomeStuff(int s)
{
return "Received input !";
}
The Register method in the WebApiConfig class has the only route as follows :
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But when I change the code to
[System.Web.Http.HttpGet]
public string SomeStuff()
{
return "Received input !";
}
The call http://localhost:53840/api/v1/LogMessage/SomeStuff works and POSTMAN displays the "Recieved input !" string in the response body.
Is there a specific calling convention for passing in int/string etc. (I tried using a [FromUri] without much success) ? I have another POST method in the controlled which takes a JObject and that seems to be working perfectly fine.
It should be something like:
[System.Web.Http.HttpGet]
public string SomeStuff(int id)
{
return "Received input !";
}
Web API matches the parameter by name. In your route template, it is defined as {id} so the action parameter name must match that.
The reason the second one works is because the id is optional and the action matches the template.

Change routing of the controller to include it under another controller

I have a controller named DummyController when i call the controller it is like DummyController/Index i want this controller to be called as maincontroller/dummycontroller/index where mainController is a different controller altogether.
Code for DummyController:
public ActionResult Index()
{
return View("~/Views/main/dummy/index.cshtml",db.Users.ToList());
}
the location of index file of Dummy Controller is main/dummy
Now the problem is when I call the dummy controller's index page i get the url as dummy/index i want the url to be displayed as main/dummy/index.
Is there any way to create child controllers? o change the url only for the specific controller
This was relatively straightforward, once I got past a simple issue.
By using a combination of [RoutePrefix("")] & [Route("")] on my controller, I was able to make this work. Attribute Routing requires a call to routes.MapMvcAttributeRoutes() in your Global.asax.cs RegisterRoutes() method.
[RoutePrefix("main/dummy")]
[Route("{action=index}/{id:long?}")]
{action=index} defines the action handling for the route, and specifies /index as the default action, if it's not supplied (ie, ~/main/dummy/ will render ~/main/dummy/index
{id:long?} specifies the id route attribute
:long is the syntax for constraining the param to the long datatype
? denotes this param is optional. (more on this here)
This could also be done using the standard MapRoute() technique:
routes.MapRoute(
name: "DummyControllerRoute",
url: "main/dummy/{action}/{id}",
defaults: new { controller = "Dummy", action = "Index", id = UrlParameter.Optional });
I forgot to add what my simple issue was..
I had 2 actions in the controller: Index() and Index(id), which would always result in the AmbiguousRouteException, and was leading me to believe my Route was defined incorrectly. Changing that to View(id) solved it. (I guess technically the route was wrong, but I didn't need to spend any more time trying to make it work that way)

writing route in mvc4 to eliminate query string

I have a situation where I am redirecting to an action that accepts 3 parameters. This I am doing like -
RedirectToAction("ProductSpecific", routeValues: new { partId = m.partId, categoryId= m.categoryId, categoryName = m.categoryName});
However, when the page loads, it contains all these parameters as query string.
Parts/ProductSpecific?partId=38&categoryId=1&categoryName=Monitor
I tried writing a route, but that didn't work. Can someone please guide on how to write a route in this scenario?
Thanks
The second argument of RedirectToAction is routeValues, so these will be appended to the querystring. Creating an extra route will still require you passing the values in the querystring, but like this: parts/productspecific/{partId}/{categoryId}/{categoryname} which i dont think you want.
If you dont want the values in the querystring, have a look at the TempData object, which is similar to session but will live until the next request.
Something like this:
public ActionResult DoSomething()
{
TempData["partId"] = partId;
TempData["catId"] = catId;
TempData["catName"] = catName;
return RedirectToAction("ProductSpecific");
}
public ActionResult ProductSpecific()
{
var partId = TempData["partId"];
var catId = TempData["catId"];
var catName = TempData["catName"];
var model = service.LoadProduct(partId, catId, catName);
return View(model);
}
Update:
For a route:
routes.MapRoute(
name: "ProductRoute",
url: "{controller}/{action}/{partId}/{categoryId}/{categoryname}",
defults: new { controller = "product", action = "productspecific"}
);
Add that route in the route.config class in app_start before your default routesm, and change your product specific method signature to accept the partid, catid and category name parameters. You can also use this from phil hack to profile your routes: Route Debugger

asp.net mvc, generate url by custom route

i have created application, where url generates depends on database values.
i parse these urls without any problem and get controller and action from database in my route handler.
but when i try to generate url, i get troubles.
in my case, it seems like:
view
#Html.ActionLink("more", MVC.Blog.Post(item.Alias)) // i use T4MVC
MyRouteConstraint
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.UrlGeneration)
{
var data = GetDataFromDbByControllerActionAndParameters(values);
if (data == null)
return false;
var valuesToRemove = new List<string>();
var path = GenerateUrlByData(data, valuesToRemove);
values.Remove("controller");
values.Remove("action");
valuesToRemove.ForEach(v => values.Remove(v)); // remove values that is already used in path
values.Add("all", path) // path = "blog/post/postalias"
return true;
}
// skipped code
}
route rule
routes.MapRoute("Locations", "{*all}",
constraints: new { all = new LocationConstraints() },
defaults: new { },
namespaces: new []{typeof(BaseController).Namespace}).RouteHandler = new LocationRouteHandler();
and as result i got url like this
localhost:8553/?Controller=Blog&Action=Post&alias=postalias
but expect like this
localhost:8553/blog/post/postalias
how can I generate url? where it should be? i think not in the constrant, but why it is invoked in this case?
In my MVC application the closest route that matches the one you have is the one below:
routes.MapRoute(
name: "MyRouteName",
url: "{SomeFolder}/{SomePageName}",
defaults: new { controller = "MyController", action = "Index" },
constraints: new { routeConstraint = new MyRouteConstraint() }
);
SomeFolder can be fixed or changed from the database while SomePageName will be changed to a different value from database. The url should be whatever URL you want to match with this route and the one that will be replaced by the value from the database. The defaults address the Controller and Action that will render the page in the end of the MVC cycle. The constraints will lead to your Match method described.
With this configuration I have URLs like www.project.com/SomeFolder/SomePageNameFromDatabase.