ASP.NET MVC : Actionlink does not navigate to correct action method of same name but different parameters - asp.net-mvc-4

I have two action methods with same name. And they both are decorated with the Http verbs 'Get' and 'Post', and also their parameter list is different.
[HttpGet]
public ActionResult Transaction()
[HttpGet]
public ActionResult Transaction(int recordId)
#Html.ActionLink("Back to Main page", "Transaction", new { recordId= Model.studentId })
Both are named Transaction, one takes a parameter while the other does not take any.
The action link intended to call the Transactions(int recordId) method when that button hit, always navigates to Transaction() but not to Transaction(int recordId).
I have two questions:
Why is my code always reaching the method which doesn't have a parameter, even though the action link has a parameter?
I have decorated my action methods with HttpPost and HttpGet, how I can include the HttpGet or HttpPost with the action link or with any other form element?

An ActionLink is rendered as an <a href="...."> HTML element and therefore is always a GET request and thus always goes to the action method decorated with [HttpGet] annotation - it does NOT look at any parameter list or anything like that.
If you want to call the POST action method, you need to have a HTML form and a submit button on that form - that will be a POST request to be handled by the method decorated with the [HttpPost] annotation ...

Related

Using attribute routing on a controller is forcing me to manage all routes

I am just getting to grips with Asp.net Core and I'm trying to set up a basic site.
I want to build an admin panel that is under a subdirectory.
I have a simple controller which was scaffolded by the EF crud feature.
So it seems that from the examples I should just be able to add a [Route()] attribute to the controller and it will prefix everything. Something like this:
[Route("Admin/Subfolder/[controller]")]
public class EventsController : Controller
{
}
But when I do that I just get an error page saying "multiple actions matched" and it lists index, details, create, etc.
I can get it working if I then go through every method and put a [Route()] attribute on it but this doesn't seem to be in line with the documentation.
It feels like I should be able to just add a prefix to the controller route without having to take over management of every route within the controller. Case in point, the POSTS are not working now and I'm not sure what the format of the route attribute should be for them.
What am I doing wrong?
You are doing it correctly. Default route attribute can be applied at the controller level. “Placing a route attribute on the controller makes all actions in the controller use attribute routing.”
Can you post complete code of your controller? There must be something else going on in there. Make sure you use HttpPost/HttpGet attribute for actions with the same name, like so:
[Route("Admin/Subfolder/[controller]")]
public class EventsController : Controller
{
[HttpGet]
public IActionResult NewEvent()
{ }
[HttpPost]
public IActionResult NewEvent()
{ }
}
Good explanation on routing can be found here

What should be the return type of WEB API Action Method?

I am developing ASP.NET Web API using .NET Core. This Web API is going to be mainly accessed by UI application (UI will be developed using ASP.NET Core MVC) but in future API may be accessed by other applications as well.
In my WEB API all methods are async.
If I want client to do content negotiation then what should be the return type of the API action method Task<IActionresult> or Task<SomePOCO>
If I want method to always return data in JSON format then what should be return type of the API action method? Should it be Task<IActionResult> or Task<JsonResult> or Task<SomePOCO> because I think all 3 would work so not sure which one is appropriate here?
If I want [the] client to do content negotiation then what should be the return type of the API action method?
To do content negotiation, return Task<ObjectResult> or Task<MyPoco>.
The framework automatically wraps POCOs in an ObjectResult; therefor, both options are equivalent, and both will obey the HTTP Accept header. You will also get content negotiation by returning any result that implements ObjectResult, such as an OkObjectResult does.
If I want [the] method to always return data in JSON format then what should be [the] return type of the API action method?
To always return JSON, return a Task<JsonResult> (or use the [Produces] filter).
See also: https://docs.asp.net/en/latest/mvc/models/formatting.html#content-negotiation
[S]o I am assuming then IActionResult is only used for MVC controller?
IActionResult is the contract for all results that a Controller returns. If your action's signature has an IActionResult return type, then your action's method body can return any result type, because all of them implement the IActionResult interface. Here is the inheritance hierarchy.
IActionResult
ActionResult
ChallengeResult
ContentResult
EmptyResult
FileResult
FileContentResult
FileStreamResult
PhysicalFileResult
VirtualFileResult
ForbidResult
LocalRedirectResult
ObjectResult
CreatedAtActionResult
CreatedAtRouteResult
CreatedResult
BadRequestObjectResult
NotFoundObjectResult
OkObjectResult
RedirectResult
RedirectToActionResult
RedirectToRouteResult
SignInResult
SignOutResult
StatusCodeResult
NoContentResult
NotFoundResult
OkResult
UnauthorizedResult
UnsupportedMediaTypeResult
BadRequestResult
See also: https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNetCore.Mvc.Core
Have a look at swagger:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger
It's better to specify ProducesResponseType attribute for method:
[ProducesResponseType(typeof(TodoItem), 201)]
public IActionResult Create([FromBody, Required] TodoItem item)
So that automatically generated swagger documentation to show the actual returned data for the method.

ASP.Net Web API Route based on HTTP Method

Is there any way to route based on the HTTP method specified in the request? I'm looking to have a GET and PUT at the same URI, but I can't seem to find the option to set the route between the two. The [HttpGet] and [HttpPut] attributes merely act as filters, so a PUT request is hitting the first action and erroring out with a 405 since it hits the GEt handler first.
What I want to do
~/User/PP GET -> UserController.GetPrivacyPolicy
~/User/PP PUT -> UserController.UpdateUserPrivacyPolicy
Whats currently happening
~/User/PP GET -> UserController.GetPrivacyPolicy
~/User/PP PUT -> UserController.GetPrivacyPolicy
(this errors out because i have a [HttpGet] filter on the GetPrivacyPolicy method)
Update:
Just to compliment what was posted below, it looks like I misunderstood how the [HttpGet] and [HttpPut] attributes work, they ARE part of the routing process. I was able to achieve my desired result with the following
[HttpGet]
[Route("~/User/PP")]
public string GetPrivacyPolicy()
{
return "Get PP";
}
[HttpPut]
[Route("~/User/PP")]
public void UpdatePrivacyPolicy()
{
return "Put PP";
}
What you'll need to do is create your controller with identically named actions but decorated with different Http method attributes
public class UserController : Controller {
[HttpGet]
public ActionResult PrivacyPolicy(int id) {
// Put your code for GetPrivacyPolicy here
}
[HttpPut]
public ActionResult PrivacyPolicy(int id, YourViewModel model) {
// Put your code for UpdatePrivacyPolicy here
}
}
Of course there are appropriate actions for the other methods e.g. HttpPost, HttpDelete, HttpPatch.

initialize simple membership in MVC 4

I have a problem with my MVC 4 application which used to work fine, but stopped for some reason, and I cannot find out why. I use simple memebrship provider and code first approach. This is my Index action method in the home controller
[Authorize]
public class HomeController : Controller
{
private IActivityRepository repo;
public HomeController(IActivityRepository activityRepository)
{
repo = activityRepository;
}
//Allow anonymous to allow to create database if there isn't one yet
[AllowAnonymous]
public ActionResult Index()
{
repo.InitializeDatabase(); //!!!!!!!!!!!!!!!!!!!!!
return RedirectToAction("ManageActivities");
}
The whole concept of mine is that if database doesn't exist it gets created in InitializeDatabase Method. Then user is redirected to ManageActivities action method which is decorated with [Authorize] attribute, what in effect takes user to login action method in AccountCotroller (out of the box in MVC4). This controller is decorated with [InitializeSimpleMembership], what fires InitializeSimpleMembershipAttribute filter.
This logic worked fine for me a while ago. Today I wanted to create a new database for testing purposes. When I create data context I call the base class with a custom name for the database like so:
public class ActivityLogContext : DbContext
{
public ActivityLogContext() : base("ActivitiesConnection")
{
}
So I've changed details for my connection string and run the application. Unfortunatelly, for some reason the code hits InitializeSimpleMemebership filter before running Index method from the home controller (even though its decorated with [AllowAnonymous]). In effect simple membership is initialized but database does not yet exist, what runs me into error.
My question is, why InitializeSimpleMemebership filter is getting released on application start if Index method doesn't require authorization?
I would eliminate the use of the InitializeSimpleMembership as discussed in this article. Move initialization to the Global.asax Application_Start method and do your initialization there also, so that it happens in the correct sequence.

MVC 4 is overwriting specific Action-Parameters

MVC 4 does present me some strange behaviour at the moment.
Imagine the following Code:
TestController.cs
public class TestController : Controller
{
public ActionResult Index(Function function, string action)
{
return View();
}
public class Function
{
public string Action { get; set; }
}
}
It seems, that when I call the URL directly through the browser (localhost:PORT/Test), the Action-Property gets automatically filled with "Index".
If the Action would be named "MySuperDuperActionWhichGetsInvokedQuiteOften", exactly this Methodname would be in the property.
Can somebody explain what MVC is doing here?
The Problem is, that I of course want to fill that stuff myself, for example through an AJAX-Query. But if MVC is filling in this property all by itself, this breaks some behaviour.
I could, of course, just rename my property and it would be working, but it would still be quite interesting what's going on.
EDIT
I would understand it that my second parameter, string action, get's filled with the method-name. But why on earth would MVC bind any property/parameter that is named the same to the request-value of it?
It is problem with default model binder. It "maps" request fields to properties in your class. There is an article of MSDN describing how does it works but to simply this situation the code will be like this:
Action = Request["action"] //where of course Request["action"] equals to name of your action