Just starting a project which is going to use a lot of WebAPI endpoints and had some questions about the routes. Since there are going to be many methods with different parameter names, the solution I've thought about is adding different routes with varying parameter names.
The problem is all the methods I define in various ApiController classes have the signature similar to
public string SomeMethod(string token, string id);
{
//method body
}
I'd like to have other methods with:
public string SomeMethod1(string token, string logType)
{
//method body
}
public string SomeMethod2(string token, string name)
{
//method body
} ....etc
I would like to avoid having to define every method with the parameter name as "id", so that the routes would match and would bind to the respective method in the ApiController class.
Is this an acceptable practice to add many routes in WebAPI routes config, so that varying parameters with different parameter name would bind to the correct method.
Would it affect the overall performance, if I've many routes in the config class?
Is there a better way to achieve what I'm trying to pull here?
It sounds like your main concern is the trade off between the a) need for multiple route definitions versus b) a single route with the 'id' parameter name. While I doubt the performance hit of many routes is a big deal I would lean toward a single route definition for the sake of having less code. You don't have to call the parameter 'id', but it would need to be the same. Perhaps something generic like 'argument':
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{token}/{argument}",
defaults: new { controller = "Blah", action = "SomeMethod" });
Related
I am having a hard time wrapping my head around custom routing in MVC Core.
I get that I need to add something here in Startup
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
But how am I supposed to get a controller to function properly?
I basically need a data details view to pull up using a string instead of an id.
So "string url" instead of "int id".
I read some articles online but everything I tried seemed to fail.
Thanks in advance.
You should be fine by adding a route constraint, telling your code, that id will be a string (word);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id}",
defaults: null,
constraints: new {Id = #"\w+" }); /* \d+ limits to only digits*/
});
Reference: http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/creating-a-route-constraint-cs
Alternativley you could use AttributeRouting and decorate your controller and action methods with the appropriate Route() annotation:
[Route("api/[controller]")]
public class HomeController : Controller
{
[Route("[action]/{name}")]
public string GetSomething(string name)
{
return foo;
}
}
You use route constraints to restrict the browser requests that match a particular route. You can use a regular expression to specify a route constraint.
The correct solution for MVC Core is to add a constraint as follows:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: null,
constraints: new { id = #"\w+" }); /* \d+ limits to only digits*/
});
In order to prevent the compile errors from happening, you need to supply a value for defaults (in this case, null), and also it should be constraints, not constraint. To prevent possible issues down the road, you should also be mindful of the case used for the id parameter.
But how am I supposed to get a controller to function properly?
I only want to underline that this is only one of various options.
Routing is used when you want to prettify the url, and manage the third slash in the url. It is the best option when the url is visible, but remember that if you are working on ajax for example you can use directly the querystring without the routing rules: controller/action?id=hello
If your need is a routing rule:
You can modify the default rule to accept also a string in the id parameter, and keep working with a method that accept an 'id'.
Add another rule that accept another parameter named for example 'code' or something that fits well for your methods that use a string research key. A
nd customize that new binding on various levels (like the default for all controllers\actions, for a single controller, ...)
You can add that custom rule also using C# Attributes in the controller.
(PROS: you have on the method the rule, so is useful to rembember, and you can import in another project the controller and all its routing rules. CONS: on large projects may be difficult understand how will interact rules that are all distributed in various files).
I have a standard set of routes, used across controllers, such as {controller}/{action}/{clientId}/{id}. All controllers are using each of these values, but each controller may use {id} in a different context.
For example, an {id} on a LabController may be "labId", and {id} on MembershipController may be "membershipId", etc. Instead of using "id" in every action method on each controller, I'd like to pass in "labId" and "membershipId" as parameters for actions in their respective controllers.
I could use [Bind(Prefix="id")] for every single action, but I was hoping there could be a way to control it at the controller level. I'm also trying to avoid multiple (nearly identical) routes for similar paths. Thanks.
EDIT: to clarify, I'm trying to bind these to parameters on my actions. Such as:
public ActionResult GetLab(int labId)
or
public ActionResult GetMembership(int membershipId)
All using the same route - just binding the {id} part as an alias for, in these cases, labId and membershipId, without having to use [Bind] every time.
You shouldn't need to bind a parameter name to the route at all. Keep in mind that the generic "id" parameter name is just a placeholder for the passed in value. So your routes become:
Lab/SomeAction/1
and
Membership/SomeAction/1
It doesn't care what the paramter name is in this case. You are thinking more in terms of query string parameters and not route parameters.
I've been assigned to develop the WebAPI controller for an application (something I had never worked with before). Everything went fine, had some basic requests like GetAllUsers(int id) just for testing reasons - the configuration itself is fine.
Now here's the issue. I have a method
GetAllItems(Carrier carrier) where Carrier is a class with a number of different parameters. As we already have a few Carrier instances in the database for testing purposes, what I've tried was querying the database, selecting the instance of Carrier based on the ID (GUID) attribute, but to no result.
Is there a way to test GET requests when the input parameter is an object, rather than a single value (such as int ID, for example) manually, with a test method or a test input parameter of some sort ?
EDIT.: Thanks everyone for the feedback, the solution to my issue was actually much easier to fix than I had expected. I would absolutely love to upvote all of you, although unfortunately my reputation is too low to do so (I'm new to stackoverflow), so I'll have to get back to doing so at some point in the near future. Cheers :)
As far as i understand your question, you want to be able to pass the Carrier's properties directly in the URL rather than in your request body.
ex:
[GET] http://localhost/entities?id=000000000000000
You controller method is this one
GetAllItems(Carrier carrier)
Carrier has a Id (Guid) property :
class Carrier {
public Guid Id { get; set; }
public string Name { get; set; }
}
Carrier is a complex object in term of WebApi model binding.
Default behavior for model binding is :
By default, Web API uses the following rules to bind parameters:
If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
see: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
Expecting a model binding with a complex object in the URL is not the WebApi default behavior.
If you want your controller method to model-bind a complex object from the URL you have to tell it.
GetAllItems([FromUri] Carrier carrier)
With the FromUri binding indicator, you can use the complex model binding from the URL
Now you can even add more properties mapping in the URL :
[GET] http://localhost/entities?id=000000000000000&name=ABC
GetAllItems will received a Carrier object populated with :
carrier.Id = 0000-00000000000-000;
carrier.Name = "ABC"
You have a routing problem here along with several misconceptions.
The default route for WebApi is:
routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
This along with certain Conventions:
GetX maps GET methods.
InsertX maps POST methods.
UpdateX maps PUT methods.
DeleteX maps DELETE methods.
When your naming convention is not in alignment with the WepApi conventions then you would need to specify the method, action name, etc.
Same happens with your routes. If you have no other route defined, then only the actions following the convention AND the default route will get bounded.
For instance:
public IEnumerable<Carrier> GetAll(){
//this will get called when using the route: /api/carriers/
}
public IEnumerable<Carrier> Get(string id){
//this will be called when using the route: /api/carriers/1
//where 1 is the carrier id
}
Will work in the CarrierController since they both are aligned with the conventions and the route.
Now, if you need a method that return ALL the items for a carrier you will need this method:
[ActionName("getItems")]
public IEnumerable<Item> GetAllItems(string id){
//where id is the carrierid
var carrierId = id;
//because you are specifying the ActionName to getItems this will match the following route:
// /api/carriers/getItems/1
}
Another option is to create a ItemsController, and add an action that return a list of items based on the carrierId, this is probably better, conceptually, but the routing principle is the same.
I want to generate URL like.. It should include two IDs with employer and job including.
I am confused and have no idea about it. I have a controller Employer.
http://localhost/Employer/[employerID]/job/[jobid]
routes.MapRoute(
"EmplyerJob", // Route name
"Employer/{empid}/job/{jobid}",
new { controller = "Employer",
action = "Job" }
);
I have made a few changes to Xander's answer. I don't think you'll want to use parameters here, as this will throw off other routes to other controllers/action methods. If you use the hard-coded "Employer" and "job" strings, you will be narrowing down what routes are analyzed by this route.
Also, you can't have an optional parameter before a required parameter.
I'm looking for some information on Routing in MVC with C#. I'm currently very aware of the basics of routing in MVC, but what i'm looking for is somewhat difficult to find.
Effectively, what I want to find is a way of defining a single route that takes a single parameter.
The common examples I have found online is all based around the example
routes.MapRoute(
"Default",
"{controller}.mvc/{action}/{id}"
new { controller = "Default", action="Index", id=""});
By mapping this route, you can map to any action in any controller, but if you want to pass anything into the action, the method parameter must be called "id". I want to find a way around this if it's possible, so that I don't have to constantly specify routes just to use a different parameter name in my actions.
Has anyone any ideas, or found a way around this?
If you want to have a different parameter name and keep the same routing variable, use the FromUri attribute like so:
public ActionResult MyView([FromUri(Name = "id")] string parameterThatMapsToId)
{
// do stuff
}
In your routes, all you need is:
routes.MapRoute(
"Default",
"{controller}.mvc/{action}/{id}"
new { controller = "Default", action="Index", id=""});
I don't think that you can do exactly what you are asking. When MVC invokes an action it looks for parameters in routes, request params and the query string. It's always looking to match the parameter name.
Perhaps good old query string will meet your needs.
~/mycontroller/myaction/?foobar=123
will pass 123 to this action:
public ActionResult MyAction(int? foobar)
I know this is centuries ago, but hope it still helps someone. I asked the same question before. I think this is what you are looking for. An answer quoted from my question post:
"The {*pathInfo} bit is called a slug. it's basically a wildcard saying "everything after this point is stuffed into a parameter called pathInfo". Thus if you have "{resource}.axd/{*pathInfo}" and a url like this: http://blah/foo.axd/foo/bar/baz/bing then two parameters get created, one called resource, which would contain foo and one called pathInfo which contains foo/bar/baz/bing."
You can construct the routes as you like
routes.MapRoute(
"Default",
"{controller}.mvc/{action}/{param1}/{param2}/{param3}"
new { controller = "Default", action="Index", param1="", param2="", param3=""});
Also, look at this post, it contains all kind of samples in the comments section
Although you still can't use the FromUri attribute, you can however use the Route attribute, like so
[Route("~/Policy/PriorAddressDelete/{sequence}")]
public ActionResult PriorAddressDelete(int sequence)
{
Policy.RemoveScheduledPriorAddressItem(sequence);
return RedirectToAction("Information", new { id = Policy.Id });
}
Technically this adds a new route, but at least it doesn't clutter up your routeconfig. It puts the route definition right by where it's used, which I like (less hunting things down).
Remember, in order to use attribute routing, this must be in your routeconfig file above your defined routes:
routes.MapMvcAttributeRoutes();