Routing to a hard-coded Controller - c#

This route mapping doesn't work:
configuration.Routes.MapHttpRoute(
"EnvironmentTargetsView",
"api/EnvironmentTargetsView/{id}/{userGuid}",
new
{
id = RouteParameter.Optional,
userGuid = RouteParameter.Optional,
});
I get the error: "No route providing a controller name was found to match request URI"
However, this route mapping does work:
configuration.Routes.MapHttpRoute(
"EnvironmentTargetsView", "api/{Controller}/{id}/{userGuid}",
new
{
Controller = "EnvironmentTargetsView",
id = RouteParameter.Optional,
userGuid = RouteParameter.Optional,
});
I am curious about why and have surfed for answers on here, but can't really figure it out. I want to hard-code that value because it is a specific route I want the API to take. My worry is by having it tokenised in the routeTemplate I can now not use a route with a similar pattern.

It's because you specified:
Controller = "EnvironmentTargetsView"
in the second code block. If you add that to the first code block it'll work and still have a hard coded value.
You could also just add something like:
[Route("/api/EnvironmentTargetsView/{id}/{userGuid}]
public void Get(int id, guid userGuid) { }
To your controller methods.

Related

C# MVC url parameter not being read

Very new to ASP.net MVC and C# in general. Experience with PHP/NodeJS mostly, a little Java.
I have a method in a controller like so:
public ActionResult ImageProcess(string fileName){
string url = "http://myurl.com/images/" + fileName + ".jpg";
//Code to stream the file
}
And when I navigate to it as "http://myurl.com/Home/ImageProcess/12345" I get thrown a 404 error by the process as it's trying to fetch the file.
If I hard-code it like so...
public ActionResult ImageProcess(string fileName){
string url = "http://myurl.com/images/12345.jpg";
//Code to stream the file
}
...It works just fine, returns my processed image as expected.
Why is this happening?
If you're using the default routes provided for ASP.NET MVC, the fix is simple: change fileName to id.
Example:
public ActionResult ImageProcess(string id) {
string url = "http://myurl.com/images/" + id + ".jpg";
}
In the file RouteConfig.cs you should see something like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "YourProject.Controllers" }
);
This is the configuration that tells the framework how to interpret URL strings and map them to method calls. The parameters for these method calls need to be named the same as in the route.
If you want the parameter to be named fileName, just rename {id} to {fileName} in RouteConfig.cs, or create a new route with a new name and defaults above the default route. But, if that's all you're doing, you might as well stick with the default route and name the parameter id in your action.
Your other option would be to use a query parameter, which would not require any route or variable changes:
link text
Look here for a nice tutorial on routing.
Either change the routing value as #johnnyRose already suggested or change the url to a get parameter, that will let the model binding find the fileName attribute. Like this:
http://myurl.com/Home/ImageProcess?fileName=12345

No action was found on the controller that matches the request

Please excuse my ignorance in this area. I have read many threads and still cannot get my routing correct.
I have a ProductsController like this:
public class ProductsController : ApiController
{
[ActionName("GetListOfStudents")]
public static List<Structures.StudentInfo> GetListOfStudents(string Username, string Password)
{
List<Structures.StudentInfo> si = StudentFunctions.GetListOfStudents(Username, Password);
return si;
}
}
I have a console test program where I have defined the route:
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/products/GetListOfStudents",
defaults: new { controller = "products", action = "GetListOfStudents" });
But when I run call
GET http://localhost:8080/api/Products/GetListOfStudents
I get the error message:
MessageDetail=No action was found on the controller 'Products' that matches the name 'GetListOfStudents'.
I have been pulling my hair out and cannot work out what the correct route should be.
Would any kind person care to help me out?
Ok- thanks for the help peeps!
This what I did to get it working:
Removed the "static" from the GetListOfStudents function.
Added the route below.
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/products/GetListOfStudents/{username}/{password}",
defaults: new { controller = "products", action = "GetListOfStudents" }
);
Thanks everyone for your help!
When registering your global api access point, you should tell the config which route to use in the following manner:
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}
defaults: new { controller = "products", action = "GetListOfStudents" });
In this sample you explicitly tell the controller it should only go to the "products" controller, you can make it generic without specifying the control or the action, just omit the defaults, like this:
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}
That should do the job :)
Your GetListOfStudents action requires two parameters, username and password. Yet, the route definition contains neither specification in the route template where the values for those parameters should come from, nor specification for those parameter defaults in the defaults: parameter definition.
So when request comes in, routing is able to find your controller, but it is unable to find the action that it can call with the request and route context that it has because it has no information for the username and password parameters.
The most important is:
ASP.Net's mvc not only seek action by name, also it will check method's signature, only the method is non-static, name matches and parameters matches, the action will be executed.
for your case, there are two ways to correct it.
one way is declare default value, mvc will use default value when parametr not found.
public List<Structures.StudentInfo> GetListOfStudents(string Username = null, string Password = null)
{
List<Structures.StudentInfo> si = StudentFunctions.GetListOfStudents(Username, Password);
return si;
}
the second way is use override
public List<Structures.StudentInfo> GetListOfStudents()
{
return GetListOfStudents(null, null);
}
public List<Structures.StudentInfo> GetListOfStudents(string Username, string Password)
{
List<Structures.StudentInfo> si = StudentFunctions.GetListOfStudents(Username, Password);
return si;
}
I had this problem and solved it by including the verb as part of the action (i.e. GetThis, GetThat) and manually creating routes. I was attempting to create routes using attributes, but that did not work. This SO question may be the answer as to why the attributes aren't working, I haven't gotten that straightened out yet. As an additional note for anyone else having the same problem, when debugging it locally, IE was crashing when the "no action found" xml was returned. Once I gave up and switched to Chrome, the message detail was returned, and it was obvious that my controller at least was being found, it was just a matter of getting the action to work...
If you want to call GetListOfStudents method without parameter you must set default value for parameter. such as
GetListOfStudents(string Username=null, string Password=null)
Otherwise you must call method with Parameters.
GET http://localhost:8080/api/Products/GetListOfStudents/Username/Password
One issue could be the order of the route declarations in your WebApiConfig.cs file. Have a look here about the precedence of routes. If you have two routes with the same amount of parameters, you may need to reorder the routes, or -- depending on how specific the route is -- hardcode the controller or action name
When sending, encode the password with base64.
Then when you about to use it decode it.
byte[] numArray = Convert.FromBase64String(password);
string Pass = Encoding.UTF8.GetString(numArray);
List<Structures.StudentInfo> si = StudentFunctions.GetListOfStudents(Username, Pass);
Works fine for me.

Can't bind to parameter

I've got the default routing:
routes.MapRoute(
"Shortie", // Route name
"{controller}/{id}", // URL with parameters
new { controller = "Ettan", action = "Index", id = "id" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Ettan", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I've got a controller: NewsController. It has one method, like this:
public ActionResult Index(int id)
{
...
}
If I browse to /News/Index/123, it works. /News/123 works. However, /News/Index?id=123 does not (it can't find any method named "index" where id is allowed to be null). So I seem to be lacking some understanding on how the routing and modelbinder works together.
The reason for asking is that I want to have a dropdown with different news sources, with parameter "id". So I can select one news source (for instance "sport", id = 123) and it should be routed to my index method. But I can't seem to get that to work.
The ASP.NET MVC Routing works using reflection. It will look inside the controller for a method matching the pattern you are defining in your routes. If it can't find one...well you've seen what happens.
So the answer is (as posted in the comments) to change the type of your id parameter to a Nullable<int> i.e. int?.

ASP.NET MVC2 Custom routing with wildcard or free text url

I have a requirement to add specific functionality to an asp.net mvc2 web site to provide addtional SEO capability, as follows:
The incoming URL is plain text, perhaps a containing a sentence as follows
"http://somesite.com/welcome-to-our-web-site" or
"http://somesite.com/cool things/check-out-this-awesome-video"
In the MVC pipeline, I would like to take this URL, strip off the website name, look up the remaining portion in a database table and call an appropriate controller/view based on the content of the data in the table. All controllers will simply take a single parameter bieng the unique id from the lookup table. A different controller may be used depnding on different urls, but this must be derieved from the database.
If the url cannot be resolved a 404 error needs to be provided, if the url is found but obsolete then a 302 redirect needs to be provided.
Where the url is resolved it must be retained in the browser address bar.
I have had a look at the routing model, and custom routing and can't quite work out how to do it using these, as the controller would not be predefined, based on a simple route. I am also unsure of what to do to provide 404, 302 back to the headers also. Perhpas I need a custom httpmodule or similar but going there went beyond my understanding.
This must be possible somehow... we did it years ago in Classic ASP. Can anyone help with some details on how to achieve this?
Well, the simplest way would be to have an id somewhere in the url (usually the first option)
routes.MapRoute(
"SEORoute", // Route name
"{id}/{*seostuff}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, seostuff = UrlParameter.Optional } // Parameter defaults
);
In your controller you'd have something like
public class HomeController {
public ActionResult Index(int id) {
//check database for id
if(id_exists) {
return new RedirectResult("whereever you want to redirect", true);
} else {
return new HttpNotFoundResult();
}
}
}
If you don't want to use the id method you could do something else like...
routes.MapRoute(
"SEORoute", // Route name
"{category}/{page_name}", // URL with parameters
new { controller = "Home", action = "Index", category = UrlParameter.Optional, pagename = UrlParameter.Optional } // Parameter defaults
);
public ActionResult Index(string category, string page_name) {
//same as before but instead of looking for id look for pagename
}
The problem with the latter is that you would need to account for all types of routes and it can get really difficult if you have a lot of parameters that match various types.
This should get you in the right direction. If you neeed some clarification let me know and I'll see if I can write a specific route to help you
Additional
You could probably do what you're looking for like
public ActionResult Index() {
//Create and instance of the new controlle ryou want to handle this request
SomeController controller = new SomeController();
controller.ControllerContext = this.ControllerContext;
return controller.YourControllerAction();
}
but I don't know any of the side effects by doing that...so it's probably not a good idea - but it seems to work.

ASP.NET MVC 2 Routing with additional consistent parameters (not just controller & action)

Currently, I have URLs that look like this:
http://www.example.com/user/create
http://www.example.com/user/edit/1
But now, I have to support multiple organizations and their users. I need to have something like this:
http://www.example.com/org-name/user/create
http://www.example.com/org-name/user/edit/1
I was having trouble getting the routes to work just perfectly, so I had to add a token to the beginning of the organization name so that routing wouldn't confuse it with a controller/action pair. Not a huge deal but my URLs look like this now:
http://www.example.com/o/org-name/user/create
http://www.example.com/o/org-name/user/edit/1
That's fine. I can live with that.
Here's where I'm running into trouble:
When I generate URLs once I have an organization selected, it's not persisting the organization name. So when I'm here:
http://www.example.com/o/org-name
...and I use Url.Action("User", "Create") to generate a URL, it outputs:
/user/create
...rather than what I want:
/o/org-name/user/create
This is what my routes look like (in order):
routes.MapRouteLowercase(
"DefaultOrganization",
"{token}/{organization}/{controller}/{action}/{id}",
new { id = UrlParameter.Optional },
new { token = "o" }
);
routes.MapRouteLowercase(
"OrganizationDashboard",
"{token}/{organization}/{controller}",
new { controller = "Organization", action = "Dashboard" },
new { token = "o" }
);
routes.MapRouteLowercase(
"DefaultSansOrganization",
"{controller}/{action}/{id}",
new { controller = "Core", action="Dashboard", id = UrlParameter.Optional }
);
It's similar to this question ASP.NET MVC Custom Routing Long Custom Route not Clicking in my Head.
I have a feeling this is going to end up being obvious but it's Friday and it's not happening right now.
EDIT:
Womp's suggested worked but would this be the best way to automate this?
public static string ActionPrepend(this UrlHelper helper, string actionName, string controllerName)
{
string currentUrl = helper.RequestContext.RouteData.Values["url"] as string;
string actionUrl = string.Empty;
if (currentUrl != null)
{
Uri url = new Uri(currentUrl);
if (url.Segments.Length > 2 && url.Segments[1] == "o/")
actionUrl = string.Format("{0}{1}{2}{3}", url.Segments[0], url.Segments[1], url.Segments[2],
helper.Action(actionName, controllerName));
}
if(string.IsNullOrEmpty(actionUrl))
actionUrl = helper.Action(actionName, controllerName);
return actionUrl;
}
EDIT:
Fixed my routes to work rather than hacking it together. The final solution didn't need the stupid {token} in the URL. Maybe this'll help someone else:
routes.MapRouteLowercase(
"Organization",
"{organization}/{controller}/{action}/{id}",
new { controller = "Organization", action = "Dashboard", id = UrlParameter.Optional },
new { organization = #"^(?!User|Account|Report).*$" }
);
routes.MapRouteLowercase(
"Default",
"{controller}/{action}/{id}",
new { controller = "Core", action = "Dashboard", id = UrlParameter.Optional }
);
Url.Action uses route values to generate the actual URL's by querying the virtual path provider and attempting to match the most specific route. In the form that you are using, you are supplying values for the controller and the action, which is as deep as most simple websites go, hence the convenient form of the method. When Url.Action queries the routing system, it only has a "controller" and an "action" segment to match.
If you give the method the rest of the routing information it needs, it will properly match the route that you desire, and will return the correct URL. Try this:
Url.Action("User", "Create", new { token = "o", organization = "organization" })

Categories