QueryString with MVC 5 AttributeRouting in Web API 2 - asp.net-mvc

I have the following code
[HttpGet]
[Route("publish/{id}")]
public IHttpActionResult B(string id, string publishid=null) { ... }
So as far as I understood,
~/..../publish/1?publishid=12
~/..../publish?id=1&publishid=12
Should work and bind both parameters but it won't work on the second case.
In the first case, publishid will not be bound.
So I do not understand why this is not working. Any idea why it is in this way?

The second case will not work because id is a required variable in the route template publish/{id}. In Web API first route template matching happens and then the action selection process.
other cases:
publish/1 - will not work as action B is saying that publishid is required. To prevent this you can change the signature of action to be something like B(string id, string publishid=null) and only id is bound
publish/1?publishid=10 - works as expected where both are bound.

Related

Attribute Routing with RoutingParameter

I currently have a set of action methods in a web api controller that use Attribute Routing.
[RoutePrefix("Paper")]...
[Route("Shape/Octagon/{id:minlength(1)}]
public IEnumerable<sampleShape> PostactionName(string id){..}
[Route("Shape/Hexagon/{id:minlength(1)}]]
public IEnumerable<sampleShape> PostactionName(string id){..}
which would work for the following URIs
api/Paper/Shape/Octagon/"1,2,3"
api/Paper/Shape/Hexagon/"3,2,1"
but becomes unusable once the id parameter becomes to long. Is there anyway to have routing use the parameter id as a form data rather than part of the URI but still keep the Route attribute.
You can use FromBody attribute to let the engine know that parameter will come from post body
[RoutePrefix("Paper")]...
[Route("Shape/Octagon"}]
public IEnumerable<sampleShape> PostactionName([FromBody]string id){..}
[Route("Shape/Hexagon"}]]
public IEnumerable<sampleShape> PostactionName([FromBody]string id){..}

ASP.NET MVC catch URL parameter in Controller

I've defined such a controller ViewProfile.
I want to use it for the next syntax to access public user info in my project.
/ViewProfile/Sammy
/ViewProfile/Billy
etc...
But I don't know how to handle the 2-nd parameter in URL.
I've tried to use:
[HttpGet]
public ActionResult Index(string query)
{
...
return View();
}
But in the debugger, string query is always empty.
I have read about routines mapping, but really don't understand how would it help me with the determination of id or other parameters.
I read in ASP.NET MVC Book that to get the parameter directly in the controller action just name it as ID.
ASP.NET MVC lets you easily do this without having to confi gure
anything extra. ASP .NET MVC’s default routing convention is to treat
the segment of a URL after the action method name as a parameter named
ID. If your action method has a parameter named ID, then ASP.NET MVC
will automatically pass the URL segment to you as a parameter.
I just tried a sample app and it worked fine for me. The string i entered in the URL did get passed on to the ID parameter in the action.
Also what i noticed is that you should provide your URL as viewprofile/index/1 or
viewprofile/index/somestring.
YOu seem to be skipping the action part.

Overloading of public functions in Controller

I am new to MVC. I was going through the asp.net site and found this link where it stated that public methods (actions) cannot be overloaded in controller classes. However in the site it stated that it can be only be possible if i use [AcceptVerbs(HttpVerbs.Post)] with one function.
Can you please explain how does AcceptVerbs helps in overloading the function.What it actually does behind the scene?
And in one of my sample application i am able to overload the function by using [HttpPost] in one function.What else can be used for overloading?
Basically the rule is that you can handle this when it is responding to different types of requests, so Post/Get/Delete. (Any of the items in the HttpVerbs enumeration)
It is due to the way that it does resolution of the method to call in the controller, and specifying the method allows it to handle resolution.
In ASP.NET MVC, incoming request url should match action of controller. In MVC request processing pipeline, first the controller action is selected, and then the parameters for it are inspected and populated. Imagine what happened if controller had two methods with same name but different signature (overloaded).The c# compiler does not complain, as it understands the code, because it can distinguish between methods based on its parameter signature. But ASP.NET MVC request matching mechanism, as mentioned above, cannot - it first does search for action and only after action is selected, it takes look at its parameters. Because of this, "Public actions in controllers cannot be overloaded" - if there're no difference between methods(actions) other than parameters, action selection in MVC will fail to unambiguously select one. This's where ActionMethodSelectorAttribute comes into play. This is the base mechanism for developers to affect the way MVC searches for valid action in specified controller. It has the method IsValidForRequest() that tells MVC wether action can be selected for usage or not. All of [AcceptVerbs], [HttpGet], [HttpPost], [HttpPut], [HttpDelete] and [HttpNonAction] derive from this attribute. And bingo - now the method overloading is possible - although actions have got the same name, one of the attributes above (or your custom attribute derived from ActionMethodSelectorAttribute) can tell MVC wchich action to select and which one to not. And MVC now unambigously knows wchich action is valid for request. Consider example
[HttpGet]
public ActionResult Index()
{
// The above HttpGet.IsValidForRequest() called internally
by mvc will return true only if request is made via HTTP GET
}
[HttpPost]
public ActionResult Index(MyModel model)
{
// The above HttpPost.IsValidForRequest() called internally
by mvc will return true only if request is made via HTTP POST
}
// And so forth with other ActionMethodSelectorAttibute s. As you see, only one action from same named ones is valid for single request when decorated with any of builtin ActionMethodSelectorAttibute

ASP.Net MVC - handling bad URL parameters

What's the best way to handle a visitor constructing their own URL and replacing what we expect to be an ID with anything they like?
For example:
ASP.Net MVC - handling bad URL parameters
But the user could just as easily replace the URL with:
https://stackoverflow.com/questions/foo
I've thought of making every Controller Function parameter a String, and using Integer.TryParse() on them - if that passes then I have an ID and can continue, otherwise I can redirect the user to an Unknown / not-found or index View.
Stack Overflow handles it nicely, and I'd like to too - how do you do it, or what would you suggest?
Here's an example of a route like yours, with a constraint on the number:
routes.MapRoute(
"Question",
"questions/{questionID}",
new { controller = "StackOverflow", action = "Question" },
new { questionID = #"\d+" } //Regex constraint specifying that it must be a number.
);
Here we set the questionID to have at least one number. This will also block out any urls containing anything but an integer, and also prevents the need for a nullable int.
Note: This does not take into account numbers that larger than the range of Int32 (-2147483647 - +2147483647). I leave this as an exercise to the user to resolve. :)
If the user enters the url "questions/foo", they will not hit the Question action, and fall through it, because it fails the parameter constraint. You can handle it further down in a catchall/default route if you want:
routes.MapRoute(
"Catchall",
"{*catchall}", // This is a wildcard routes
new { controller = "Home", action = "Lost" }
);
This will send the user to the Lost action in the Home controller. More information on the wildcard can be found here.
NB: The Catchall should reside as the LAST route. Placing it further up the chain will mean that this will handle all others below it, given the lazy nature of routes in ASP.NET MVC.
Here is some useful infromation that might help.
If you have a action method
public ActionResult Edit(int? id)
{}
then if someone types in
/Home/Edit/23
the parameter id will be 23.
however if someone types in
/Home/Edit/Junk
then id will be null which is pretty cool. I thought it would throw a cast error or something. It means that if id is not a null value then it is a valid integer and can be passed to your services etc. for db interaction.
Hope this provides you with some info that I have found whilst testing.
In ASP.NET MVC, you can define a filter implementing IActionFilter interface. You will be able to decorate your action with this attribute so that it will be executed on, before or after your action.
In your case, you will define it to be executed "before" your action. So that, you will be able to cancel it if there is an error in the passed parameters. The key benefit here that you only write the code which checking the passed paramaters once (i.e you define it in your filter) and use it wherever you want in your controller actions.
Read more about MVC filters here: http://haacked.com/archive/2008/08/14/aspnetmvc-filters.aspx
You can specify constraints as regular expressions or define custom constraints. Have a look at this blog post for more information:
http://weblogs.asp.net/stephenwalther/archive/2008/08/06/asp-net-mvc-tip-30-create-custom-route-constraints.aspx
You will still need to deal with the situation where id 43243 doesn't map to anything which could be dealt with as an IActionFilter or in your controller directly.
The problem with that approach is that they still might pass an integer which doesn't map to a page. Just return a 404 if they do that, just as you would with "foo". It's not something to worry about unless you have clear security implications.

Asp Net Core 2.2 on random address goes to controller instead of 404 Not Found

If the address is well formatted everything is fine.
But when I type random string into the address bar, asp net core routes to the same controller and in particular to Index(model)
[HttpGet]
public IActionResult Index() { ... }
[HttpGet({model})]
public IActionResult Index(ViewModel model) { ... }
instead of returning 404 Not found.
Is the problem with the controller?
Your HomeController is mapped to "" or the empty route, and then this second Index action requires basically any route portion. So, /foobar literally routes to this Index action.
There's a number of problems/misunderstandings here. When you specify {model} in the route, you're telling the framework that this is a route param. There's effectively no way to actually ever bind your model, because you can't actually post a representation of ViewModel within a route path.
Second, there is no validation of route params during routing. In other words, ASP.NET Core doesn't look at you action, see that it needs to be an instance of ViewModel, realize that foobar in /foobar is not a ViewModel and then returns 404. It merely sees that there's a potential route that takes something in that route segment, and then attempts to load up the corresponding action. It's on the modelbinding phase where things will fail, but that's after the routing is already done.
You can use route constraints to add a layer of validation. For example, {foo:int} would only match if the value in that route segment was something that could be cast to an int, but you can't really do that for something like ViewModel.
Third, you shouldn't be sending an entire model via a GET. GET doesn't have a body, and complex objects should go in the body of the request. You should be using POST here instead.
Long and short, change your action to:
[HttpPost]
public IActionResult Index(ViewModel model) { ... }
And you'll be fine.

Resources