C# configure RESTful routes - c#

I want to have a routes
{domain}/v1/images/{id} and {domain}/v1/images/{email}.
The first route is working (for example: v1/images/1?size=100) but when I try the second (for example: v1/images/foo#domain.com?size=100 I get this error:
The parameters dictionary contains a null entry for parameter 'id' of
non-nullable type 'System.Int32' for method
'System.Net.Http.HttpResponseMessage Get(Int32,
System.Nullable`1[System.Int32])' in '....ImageController'. An
optional parameter must be a reference type, a nullable type, or be
declared as an optional parameter."
I have my routes configured like this:
routes.MapHttpRoute(
name: "Image",
routeTemplate: "v1/images/{id}",
defaults: new { controller = "Image", size = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "ImageByEmail",
routeTemplate: "v1/images/{email}",
defaults: new { controller = "Image", size = RouteParameter.Optional }
);
Is it possible to get the image by id or email?

Router has no chance to identify whether v1/images/foo#domain.com?size=100 goes to /v1/images/{id} or /v1/images/{email}. It silently chooses /v1/images/{id} and tries to parse "foo#domain.com" as int, parsing fails and since id parameter (int id) is not optional (as opposed to (int? id)) it throws an error.

routes.MapHttpRoute(
name: "Image",
routeTemplate: "v1/images/{id}",
defaults: new {
controller = "Image",
size = RouteParameter.Optional,
id = #"\d+" }
);
The #"\d+" will validate the id as a number.

Related

Basic Conventional routing asp.net webapi

I have a simple query on ASP.Net Web API Routing. I have the following controller:
public class CustomersController: ApiController
{
public List<SomeClass> Get(string searchTerm)
{
if(String.IsNullOrEmpty(searchTerm))
{
//return complete List
}
else
{
//return list.where (Customer name contains searchTerm)
}
}
}
My routing configuration (Conventional) looks like this:
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate:"api/{controller}/{id}"
defaults:new {id = RouteParameter.Optional});
config.Routes.MapHttpRoute(name:"CustomersApi",
routeTemplate:"api/{controller}/{searchTerm}"
defaults:new {searchTerm = RouteParameter.Optional});
If I hit the url:
http://localhost:57169/api/Customers/Vi
I get a 404-Not found
If I reverse the order of the routes, it works.
So the question is in the first case, is it matching the first route (DefaultApi)? If not, why is it not trying the second route?
This route template
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate:"api/{controller}/{id}",
defaults:new {id = RouteParameter.Optional}
);
match your URL because Id can be any type : string, int etc. So your URL respect this template and this route is chosen.
To make this template more restrcitive and make ASP.Net Web API to go to the next template you need to add to it some constraints by saying something like "Id parameter must be a numeric type for this template". So you add constraints parameter like below:
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Required}, // <- also here I put Required to make sure that when your user doesn't give searchTerm so this template will not be chosen.
constraints: new {id = #"\d+"} // <- regular expression is used to say that id must be numeric value for this template.
);
So by using this URL http://localhost:57169/api/Customers/Vi the above template will be skipped and the next will be chosen.

1-Named argument specifications must appear after all fixed arguments have been specified

I have faced this problem while working on project, I can't understand how to fix this problem
routes.MapRoute(
name: "MoviesByReleaseDate",
url: "movies/byreleasedate/{year}/{month}",
defaults: new
{
controller = "Movies",
action = "ByReleaseDate",
},
new { year = #"/d={4}", month = #"/d={2}" }
);
You call the MapRoute method with 3 named arguments (name, url, defaults) and the fourth argument is unnamed, which is not allowed.
Either name also the last argument or use only unnamed ones in the same order as it is specified in the MapRoute method.
See some explanation here.
If you want to use multiple arguments to your MapRoute method, you can just don't specified the name of the other arguments like this:
routes.MapRoute(
"MoviesByReleaseDate",
"movies/byreleasedate/{year}/{month}",
new { controller = "Movies", action = "ByReleaseDate" },
new { year = #"\d{4}", month = #"\d{2}" }
);

Url.Action generates a wrong url

I use the Url.Action method to generate a Url string.
Thats the result:
"/Home/GetRejectTest/0?IsSelected=False"
The controller and action name are correct but the query parameters are screwed up. Is this because The action does not have a RouteAttribute thus query parameters are generated?
My action:
public ActionResult GetRejectTest(Test test)
{
return new EmptyResult();
}
Test class has those 3 properties Id, Name, IsSelected.
My route definition:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
The url your generating (/Home/GetRejectTest/0?IsSelected=False) is correct for your route definition. You passing a new instance of Test to the Url.Action() method, which
Internally builds a dictionary based on the name and value
(using.ToString()) of each property in your model - i.e.
controller=Home, action=GetRejectTest, id=0, Name=null, IsSelected=False
Then searches your route definitions for a match which it finds
(url: "{controller}/{action}/{id}") and updates the placeholders
(which at this point generates /Home/GetRejectTest/0) but your
route definition does not have url parameters for Name and
IsSelected so these are added as query string parameters (because
Name is null, a query string for that propery is not generated)
so the result is now /Home/GetRejectTest/0?IsSelected=False
You have not indicated what result you're actually expecting, but creating specific route definitions will solve most cases. For example if you want
/Home/GetRejectTest/0/false
or /Home/GetRejectTest/0/false/someName if the value of Name is not null, then you can create an additional route (which must be before the default route)
routes.MapRoute(
name: "Test",
url: "Home/GetRejectTest/{id}/{isselected}/{name}",
defaults: new { controller = "Home", action = "GetRejectTest", name = UrlParameter.Optional }
);
Note that because Name is typeof string and therefore can be null, the {name} placeholder needs be the last one and marked as UrlParameter.Optional (otherwise it will revert back to using query string parameters)

ASP.NET MVC routing without unnecessary text

I have a problem with my MVC. Think like a URL.
domain.com/product/any-product/1
domain.com/category/any-category/1
Can I change url like this?:
domain.com/any-product/1
domain.com/any-category/1
Also can I delete id?
context.MapRoute(
name: "category",
url: "category/{category_name}/{category_id}",
defaults: new
{
controller = "Cateogory",
action = "Index",
Area = "Shop"
}
);
context.MapRoute(
name: "product_detail",
url: "product/{category}/{product_name}/{product_id}",
defaults: new
{
controller = "Product",
action = "ProductDetail",
Area = "Shop",
product_name = UrlParameter.Optional,
product_id = UrlParameter.Optional
}
);
No, you can't do it exactly like that, but you can get closer. Think for a second about what you're saying you want:
/any-product/ => ProductController
/any-category/ => CategoryController
Now, logically speaking, how is MVC supposed to sort out which URL is referring to a product and which is referring to a category?
You could do something like:
/any-category/ => CategoryController
/any-category/any-product/ = > ProductController
Now, there's some way to for MVC to work out that the URLs are different. If there's something after the category part of the path, then it's a product.
As for removing the id portion, that depends. The point of the id in the first place is that you need some sort of unique identifier for a record that should be loaded. If "any-product" is a string that can uniquely identify the record (you have column in the database with that value and there's only one with that value), then you can use that string to look up the entity instead. Then, no, you no longer need the id portion of the URL. However, if the string is not unique or doesn't correspond to data in the database, then you need the id to locate the record, and it cannot be removed.

MVC 4 Default Parameter Value

In MVC 4 application while defining routing I can provide a list of default parameters. What value shall I provide for optional parameter: UrlParameter.Optional or empty string?
Examples:
routes.MapRoute("simple", "{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional});
routes.MapRoute("simple", "{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = ""});
Is there any difference between id = "" and id = UrlParameter.Optional in above examples?
Please note that some controller actions will be using id (of type string) and some will be parameterless.
The difference is subtle and but almost unimportant
UrlParameter.Optional means a null will be passed to the Action Method in lieu of a value.
id = "" means a default value of "" (not null) will be passed to the Action Method.
In both cases, not including an id parameter in your route will NOT stop the MVC framework from finding the right method.
In the case of UrlParameter.Optional, you should make all relatable Action Methods take a nullable parameter
Type matters
You should NOT apply id="" to routes that use ints.

Categories