How can I get an some entity ID from view? - c#

Can I run these get and post actions with just single button? I get an id from my view then I just wanna run my post action.
I've already tried to getting id with parameter and put it in session but couldn't do it with one submit button
[HttpGet]
public ActionResult JoinEvent(int id)
{
Session["EventID"] = id;
return View();
}
[HttpPost]
public ActionResult JoinEvent(EventPerson eventPerson)
{
Person per =(Person) Session["Login"];
eventPerson.PersonID = per.PersonID;
eventPerson.EventID = (int)Session["EventID"];
eventPerson.TotalPerson = eventPerson.TotalPerson + 1;
eventManager.Join(eventPerson);
return View();
}
and this is my view
<td class="buy_link">
#Html.ActionLink("Join","JoinEvent","Join",item.EventID,null)
</td>

As what you posted here, it looks like the EventId can be set freely (without any security restrictions) by the client by simply navigating to GET /JoinEvent?id=100.
If security isn't an issue, you can do it by many many ways
Passing the event id to the client, which in turn will give it back to the server.
How ? Here is one way
Passing it as part of EventPerson model, which you will need to add a new property inside of that model.
ActionResult should: return View(new EventPerson { EventId: id })
Decorating view top row (after import statments) with the following #model EventPerson, this will tell the view that now he represents this type. (Which was passed by the GET action)
Now we should decide if we want to pass it back to the server as query string parameter or a body parameter ? Because we're doing a semantic "Insert" action, in most cases the practice asks us to pass it as part of the body. thus, you should use an hidden text box to pass EventId parameter.
#Html.Hidden("EventId", #model.EventId))
"EventId" is the property we want to bind on our POST server action EventPerson newly property. (Point#1)
#model.EventId is the value we took from the server and passed it into the View (Point#2)

Technically you cannot. When you create an anchor tag using action link, it creates a link to the specified URL. When you click on that link that fires a GET request. That will not POST data to the action method. If you need to POST data, you need to either submit a form or you can use Javascript/jQuery to do that. You can collect the data and send it as a POST using Js/jQuery. But just the anchor won't send a POST method.
Refer this

Related

How do I send form data to HttpPost Action with Postman in ASP .Net Core MVC

Out of simple curiosity I would like to Post data from my MVC app to my local database with Postman. Unfortunately, I encountered a certain obstacle and cannot figure out how to solve it.
The general idea is that I have a controller X with an Edit method accepting YViewModel as the only parameter.
XController : Controller
{
//Post
IActionResult Edit(YViewModel vm)
{
//Code
}
//Get
IActionResult Edit(int id)
{
//Code
}
}
YViewModel
{
public int Id { get; set; }
//Other fields below
}
In the Edit method with an HTTP GET Verb I am returning the respective view with the YViewModel containing all the required fields. All the fields (except Id) are assigned to form inputs.
The Id is only bound to the VM model. Binding works perfectly on the page, but I cannot compose the right Postman Request.
I know that I can try localhost..../X/Edit with form-data and then assign all the form fields. But where do I fit Id in that request?
Under the Body tab select x-www-form-urlencoded. For Key use Id. For Value use whatever you want to test with.
I assume you would want to post other YViewModel fields for testing.
Ensure you are creating a POST request.
If Id as the Key doesn't work you can load the page in your browser and look for the name attribute of the Id hidden field. Then use that in Postman.

Does ValideAntiForgeryToken prevent falsifying of POST parameters?

I somewhat understand how [ValidateAntiForgeryToken] prevents CSRF and I have read through this question, but I'm unsure whether this would prevent someone from falsifying the parameters for a form post.
ItemList has an Items property that is a collection of items and a User property that is a reference to the ApplicationUser that it belongs to. Item has an ItemList property that is a reference to the list it belongs to. Here are the Add methods in the ItemController:
// GET: Item/Add/4 (Adds new Item to the ItemList with ID=4)
public ActionResult Add(int? itemListId)
{
// Gets the current user and the ItemList that the Item will be added to
UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
ApplicationUser currentUser = userManager.FindById(User.Identity.GetUserId());
ItemList itemList = db.ItemLists.Find(itemListId);
// Makes sure that ItemList exists and belongs to the user
if (itemList == null || itemList.User != currentUser)
{
return View("InsufficientPerm");
}
ViewBag.ItemListId = itemListId;
return View();
}
// POST: Item/Add/4 (Adds new Item to the ItemList with ID=4)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add([Bind(Include = "ID,Name")] Item item, int? itemListId)
{
if (ModelState.IsValid)
{
ItemList itemList = db.ItemLists.Find(itemListId);
item.ItemList = itemList;
db.Items.Add(item);
itemList.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
My question is whether [ValidateAntiForgeryToken] would prevent a user from falsifying the itemListId parameter during the post, or if I would need to put another if (itemList == null... check in the post method.
Edit: Here is the logic that I am looking at right now:
The use of ValidateAntiForgeryToken forces a user to access the first method (therefore loading the view) in order for a post to be accepted. If they don't load that view, then there will be no anti-forgery token.
The user will go to the form webpage (let's say http://foo.bar/Item/Add/3)
The user will fill out and submit the form, which would call the post method (in this case itemListId=3 because that's the webpage that was accessed)
There is no way for a user to pass a different itemListId to the above step, because it is passed by the webpage when they submit the form
Now, please let me know if there is something wrong with what I have stated above, or if that logic is correct (meaning that I don't need to check the validity of itemListId during the post). PLEASE give an example or a link to clarify where my logic is incorrect
There is no way for a user to pass a different itemListId to the above step, because it is passed by the webpage when they submit the form
This is false. The AntiForgeryToken will not protect you from altered data.
Make a GET request to Item/Add/4. I assume you will have a form on this page. You can include the #Html.AntiForgeryToken().
Use your browser's debug tools to inspect the form.
Now you can edit the action attribute to directly modify Item/Add/5 the value.
Or modify any <input> field value.
Put a debug break on your POST action and you will see the altered values when you submit the form.
The above is not the only way to tamper with the data. Therefore, you should always validate any input.
[Authorize, ValidateAntiForgeryToken]
[HttpPost]
public ActionResult NukeMyBankAccount(int accountId)
{
var account = db.GetAccount(accountId);
// validate
if (CurrentUser.Id != account.Owner.Id)
{
return RedirectToAction("Unauthorized");
}
else
{
db.NukeAccount(accountId, areYouSure: true);
}
...
}
No. The ValidateAntiForgeryToken attribute has absolutely zero impact on the values each form field may contain. It also has absolutely no idea what data belongs to which user.
ValidateAntiForgeryToken dont prevent falsify, it prevent that the request becomes from another place.
The ValidateAntiForgeryToken check if __RequestVerificationToken exists in the form and validate it, so whatever params is in the body of the request, if the __RequestVerificationToken is invalid will break and dont returns a 200 OK, so to use it just put the Helper #Html.AntiForgeryToken() inside the form and the [ValidateAntiForgeryToken] filter in the action.
So see this article about that: How AntiForgeryToken works
The CSRF attack consists in deceive victim to click in link or load a image where the URL is of a system that victim is logged, but attacker do not know the victim credentials.
The simple case, the attacker know that the HTTP GET request in the target system change some information. To attack, just send the e-mail or link to a user of the target system with a image where the URL point to the target system.
To prenvent this, a good pratice is do not create URL that make changes in data of the system through HTTP GET request.
In complex case, the attacker create a page with a form whose action point to a URL of the target system with POST method.
In this case, use the ValidateAntiForgeryToken is fundamental. Because the token generated by the target system is known only by the target system and can be validated.
So, the ValidateAntiForgeryToken only validate if the request was originated from the target system. Don't have any relationship with others parameters in the form.

From action to action to _Layout

I have a layout page that displays simple messages to the user.
I have a People Controller with an Index action/view that displays a list of people and I have an Edit action/view for a person.
When the user successfully updates and saves a person's details, I want to redirect from the Edit action to the Index page but display a message such as "The person was successfully updated".
I've tried adding things to the ViewBag but it doesn't persist.
I have a ViewModel Base class, but don't know if/how to set the Index's ViewModel from the Edit action.
How can I achieve this where messages are passed from various places to be used in _Layout?
You can use TempData to accomplish this. Values stored in TempData will only persist across one Http request, making it ideal to store these types of messages.
Edit POST Action
[HttpPost]
public ActionResult Edit(EditPersonModel model)
{
// .. Your code to edit the person ..
TempData["message"] = "The person has been updated.";
return RedirectToAction("Index", "People");
}
Your View
#if (TempData["message"] != null)
{
<p>#TempData["message"]</p> // Displays the message
}
You can build an entire notification system that uses TempData and ViewData (controller extensions, etc), but this is the simplest way to do it.

MVC Form Submit - Redirecting to an action that is not accessible from the browser directly

I am learning MVC 3 after hours right now and last night I ran into an issue that seems like it would be very simple to solve but I can't seem to locate a solution for it.
I have a simple contact form. The end user fills out the form and submits it. When they submit the form I redirect the end user to a different action in the same controller which renders an "inquiry submitted" page/view which is basically a "Thank you page".
The controller is setup like so.
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactInfo contactInfo)
{
if (!ModelState.IsValid)
{
return View();
}
//perform some business logic
return RedirectToAction("InquirySubmitted",contactInfo);
}
public ActionResult InquirySubmitted(ContactInfo contactInfo)
{
return View(contactInfo);
}
The problem:
I do not want end users navigating directly to the InquirySubmitted action via the browser.
I only want the ContactUs action in the controller to be able to send users to the InquirySubmitted View.
I have attempted to make the InquirySubmitted action private so that only the controller can call it like so:
private ActionResult InquirySubmitted(ContactInfo contactInfo)
But this produces an error which I fully understand because I am forcing the browser to request InquirySubmitted by using RedirectToAction().
So my question is simply: What is the best "MVC 3 style" solution to this issue.
You will need to put logic in your InquirySubmitted ActionResult in order to prevent users from viewing the page if they are not supposed to.
You are already passing the InquirySubmitted method your model (ContactInfo). Could you simply inspect the data passed to the method and if it is absent then redirect the user to an error page (or some other page of your choice)?
An alternate solution would be to set a boolean in session that indicates that the user completed the "ContactUs" form. Then you could check for that session object within InquirySubmitted.
First, I would have to say.. Who cares if someone can navigate directly to the Inquiry submitted page? Is there any confidential information, or something sensitive there? If not, so what? What does it hurt?
However, if you're determined to do so. The answer to your question of "How to make an action not accessible directly from the browser" is that You can simply use Html.Action() to render the page, and then decorate the action method with a [ChildActionOnly] attribute.
This doesn't actually solve the problem though, since making the action indirectly accessible only answers your question, not solves your problem. Ultimately, you need to redirect the user to a url to load the page, so you will need some logic that determines if they can view the page or not. This is
Not sure if this still applies in MVC3, but in MVC2 it worked.
your global.asax file has your url structuring in it. You can add your InquirySubmitted to the list of urls that isn't accessible there.

Make HttpPost request to an action method in an ASP.NET MVC controller

I am trying to build a functionality where I need to a create a candidate's profile in our application. There are two steps/UI's to create a candidate's profile:
1 - Create template...where the user enters candidate's information.
2 - Preview template...where the user will be shown a preview of how their profile would look like once they add the profile to our system.
I have already created the views to support these UI's via a controller called "CandidateController" which contains few action methods:
1- [HttpGet] "Create" that returns a Create template.
[HttpGet]
public ViewResult Create()
2- [HttpPost] "Preview" that returns a Preview template.
[HttpPost]
public ActionResult Preview(ProfileViewModel viewModel)
Now what I need to implement is to have a button/link in the Create template that would call the action method [HttpPost] Preview in the controller.
Challenge
I am also wondering if there is a way that the model binder would load the ViewModel object for me if am able to call the HttpPost Preview action method from the first create template.
I am looking for a suggestion/help to how to best achieve this kind a functionality.
Any help will be deeply appreciated.
Challenge I am also wondering if there is a way that the model binder
would load the ViewModel object for me if am able to call the HttpPost
Preview action method from the first create template.
You could use either a standard form or an AJAX call to invoke the Preview POST action and pass all the property values of the view model then. All the values you pass in this request will be the values that will be bound by the default model binder. Here's an article explaining how the default model binder expects the parameters to be named for more complex structure such as lists and dictionaries.
Example with AJAX:
$.ajax({
url: '#Url.Action("Preview")',
type: 'POST',
data: { Prop1: 'value 1', Prop2: 'value 2' },
success: function(result) {
// TODO: do something with the result returned from the POST action
}
});
If you don't want to use AJAX you could use a standard form with hidden fields:
#using (Html.BeginForm())
{
#Html.Hidden("Prop1", "value 1")
#Html.Hidden("Prop2", "value 2")
...
<button type="submit">Preview</button>
}
OK so here are the options that I had to get around:
As Darin suggested you may go with the unobtrusive way by using $.ajax(options), however the thing is you might want to go this way only if you want to do a partial page update or if you want to work on updating/dumping new html in the same view.
And if you don't want to use the Ajax, instead of using Hidden fields, you can simply use the TempData property in MVC, this is how I implemented my targeted functionality using TempData. p.s.below...
[HttpPost]
public ActionResult Create(ViewModel viewModel)
{
this.TempData["profile"] = viewModel;
return RedirectToAction("Preview");
}
public ActionResult Preview()
{
if (TempData["profile"] != null)
{
return View((ViewModel)TempData["profile"]);
}
// Handle invalid request...
return null;
}
So, this solution worked pretty well for me, where I did not write any JavaScript or unnecessary HTML. AND thanks Darin for directing me to a starting point.

Categories