I am learning asp.net mvc framework
I create page with form. Data from this form going into database.
Then i return this page to user with all data from database. Easy
Ok.
Display page with form. Ok
Write data from this form to databse. Ok
Read data from databse and display it to user. Ok
Ok. But, then user refresh page, post method executes again with same form data and another data write to database. WTF?
Here code:
Controller:
public class MainController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View(db.Entries.ToList());
}
[HttpPost]
public ActionResult Index(Message msg)
{
db.Entries.Add(msg);
db.SaveChanges();
return Index();
}
private MessagesContext db = new MessagesContext();
}
View:
#using BasicWeb.Models
#model List<Message>
#{
ViewBag.Title = "Index";
}
<h2>Сохранялка</h2>
<form method="post" action="">
<fieldset>
<legend>Введи бурду</legend>
<input type="text" name="UserName" maxlength="512"/>
<input type="submit" value="ВВОДИ МЕНЯ"/>
</fieldset>
</form>
<br />
#foreach(Message item in #Model)
{
<p>#item.UserName</p>
}
By refreshing the page after a POST request, you are actually re-submitting your form.
As a better practice, after submitting the form, make sure to redirect the user to the same form but as a GET request.
Read more about it here: Post/Redirect/Get (PRG)
You may want to track if the request is because of the page refresh.
Following may be helpful:
Page Refresh Causes Duplicate POST in ASP.NET Applications
You will need to check if the request is because of postback or pagerefresh.
Related
.NET Core v6
MVC Web App with Entity Framework Core
I scaffolded a database table and it created a "db context", then I scaffolded default Controller and Views.
On the home page, I want to create a search box where you put in the primary key, or id. On submit, I want the value passed to the default Edit controller. Essentially, I want someone to be able to pull up a single record to Edit, if they know the primary key (id).
Here is what I have for a search box
<form asp-controller="MyTables" asp-action="Edit">
<p>
PIN: <input type="text" name="id" />
<input type="submit" value="Find" />
</p>
</form>
Here is the default Edit method inside the Controller.
// GET: MyTables/Edit/XYZ
public async Task<IActionResult> Edit(string id)
{
if (id == null)
{
return NotFound();
}
var MyTable = await _context.MyTables.FindAsync(id);
if (MyTable == null)
{
return NotFound();
}
return View(MyTable);
}
When I run this, it doesn't work, but I don't know how to set it up. How can I make this work? The only way I've been able to pull a record is by adding method="get" to the form, but then it creates a URL structure using "id" as a parameter like this localhost/MyController/Edit?id=XYZ. This structure won't work because I'm unable to save the record when I've navigated to the page in this way.
I appreciate any pointers or ideas.
Thank you
EDIT AFTER COMMENTS:
It has been suggested that I could go ahead and use the method="get", thereby selecting the correct record. The URL would be in this format: localhost/MyController/Edit?id=XYZ. A proposed solution is to fix my POST action to be able to save the record when using such URL paths. I'm not sure why, but it will only save right now if the URL path is in this format: localhost/MyController/Edit/XYZ (no parameter ?id =).
Here is a version of my default POST Edit action in the controller.
// POST: MyTables/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(string id, [Bind("...,...,...,...,...,...,PrimaryKeyField")] MyTable myTable)
{
if (id != myTable.PrimaryKeyField)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(myTable);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MyTableExists(myTable.primaryKeyField))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(MyTable);
}
In the Edit view, to initiate the POST action, here's some of the Edit Form markup:
#model myApp.Models.MyTable
...
<form asp-action="Edit">
...
<div class="form-group">
<label asp-for="PrimaryKeyField" class="control-label"></label>
<input asp-for="PrimaryKeyField" class="form-control" />
<span asp-validation-for="PrimaryKeyField" class="text-danger"></span>
</div>
...
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
The reason I'm saying it won't Save is when I click Submit on the Edit form, it brings up this HTTP error:
This localhost page can’t be found. No webpage was found for the web address: https://localhost:7020/MyTables/Edit
HTTP ERROR 404
In the Edit View, I added a reference to the Model.
#model myApp.Models.MyTable
In the form, I added a Tag Helper.
<form asp-action="Edit" asp-route-id=#Model.PrimaryKeyField>
This is working.
I noticed in the URL pattern of /Edit/XYZ, the form's asp-action uses this pattern. I noticed with the URL pattern of /Edit/?id=XYZ, the form's action uses /Edit with NO ID. Therefore, I figured out a way to add the id to the form's action by using the asp-route-id tag helper. The form submits the data properly now.
I am building a website with WebMatrix. I would like users to enter their name in the main page and after redirection their name will be shown in the results of another form. But my code is not working.
This is a snippet of the main page:
#{
if (IsPost) {
PageData["fullname"] = String.Format("{0} {1}", Request.Form["mainForename"], Request.Form["mainSurname"]);
PageData["redir"] = Request.Form["goTo"];
}
}
<form name="mainForm" id="mainForm" method="post" action="foo.cshtml" onsubmit="return mainValid(this);">
<h2>Please enter your name:</h2>
<label for="mainForename" class="label">Forename:</label>
<input type="text" name="mainForename" id="mainForename">
<label for="mainSurname" class="label">Surname:</label>
<input type="text" name="mainSurname" id="mainSurname">
<input type="submit" name="goTo" value="Go to Form 1">
<input type="submit" name="goTo" value="Go to Form 2">
</form>
This is a snippet of the page that the main page directs to:
#{
if (IsPost) {
var display = PageData["fullname"];
}
}
<form name="form1" id="form1" method="post" onsubmit="return Valid(this);">
<!-- some HTML code -->
<input type="submit" name="submit" value="Get results">
<p>#Html.Raw(display)</p>
</form>
But whatever value I have submitted in the mainForm, PageData["fullname"] and PageData["redir"] seem to have no values. What is the problem?
Any help would be appreciated.
I think PageData is only useful when combining subpages into a single page.
Instead, try the Session object where you are using PageData. Session will be available for all that user's pages.
So where you have PageData["fullname"] use Session["fullname"]
For more details, see http://www.mikesdotnetting.com/article/192/transferring-data-between-asp-net-web-pages
I find something that's not quite good in your code:
Why your form action is set to a cshtml file? It has to be an action in a controler;
Why are you using "hardcoded" form tag? Use #using(#Html.BeginForm('name', 'action', 'controller'...)
Why do you need WebMatrix? 1st - its pretty old, 2nd it for grids - you have a form.
Use a model, and use #Html.TextBoxFor(x=>x.UserName) inside the #Html.BeginForm.
Then post the form in the action you are posting to redirect to another page that contains the 2nd Form, and have a model. The post action should look somehow like this
[HttpPost]
public ActionResult RedirectToAnotherForm(MyModel model)
{
return View('SecondFormView', new SecondFormModel{
userName = model.name
})
}
I'm trying to submit a simple form for my ASP.NET MVC website, but I keep getting a StackOverFlow Exception. I don't understand what I'm doing wrong; it should be a pretty straightforward process...
Here is the form code:
<form action="/Home/SubmitAnnouncements" method="post">
<textarea name="announcementcode" style="width:100%; height:800px">#Model.RawAnnouncementCode</textarea>
<div id="find_account" class="two columns"><input id="find_account_btn" class="link_button" type="submit" name="Submit" value="Submit New Announcements" /></div>
</form>
And here is the Controller code for the action:
[HttpPost, ValidateInput(false)]
public ActionResult SubmitAnnouncements(string announcementcode)
{
SubmitAnnouncements(announcementcode);
return RedirectToAction("Index", "Home");
}
This problem stated when I added [HttpPst, ValidateInput(false)], but I need to do that, because the user is submitting raw HTML code. Any ideas anyone?
The ValidateInput attribute didn't cause your error, you just never reached the point in your code that causes the StackOverflowException until it was added.
If you look at the first line of your action, you'll notice you're calling the same method.
I have a simple MVC application that retrieves DB Server, database, username and password from the user to store in an XML file. I want to add a "Test Connection" button to the screen and have it execute a method on the controller called TestConnection. The problem is the TestConnection method resets all the information on the screen when clicked since the View is being returned with no model. (because a GET operation is occurring). Here is my code:
From the Controller (named FrameworkConfigurationController.cs)
public ActionResult TestConnection()
{
return View("Index");
}
[HttpPost]
public ActionResult TestConnection(FrameworkConfigurationViewModel viewModel)
{
// TODO: Test will occur here
viewModel.DbConnectionMessage = string.IsNullOrEmpty(viewModel.DatabaseName) ? "Connection unsuccessful" : "Connection successful";
return View("Index", viewModel);
}
From my View (FrameworkConfiguration/Index):
#model Framework.ViewModels.FrameworkConfigurationViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>FrameworkConfigurationViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ServerName)
</div>
#* Edited for brevity *#
<button type="submit" onclick="location.href='#Url.Action("TestConnection")'">
Test Connection</button>
#Html.ValueFor(model => model.DbConnectionMessage)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to Dashboard", "Index", "Home")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Admittedly, I am new to MVC programming. I'm coming from a Silverlight/MVVM background so the concept is not foreign to me...the disconnected nature of web programming is. I've followed some tutorials out there, but none of them seem to cover this type of thing - every example is contrived. I know how to do this with webforms and code-behind, but I would like to accomplish this with MVC.
Is there some way to "force" the POST operation instead of GET? I was under the impression that
<button type="submit">
accomplished that. Perhaps the implementation of the onclick I have written isn't correct. I am sure this is HTML 101 stuff, but I can't seem to find a simple answer to this.
Thanks for any help you can offer,
Jason
Why not have the Test Connection button trigger an Ajax action to call the controller, and display the result?
That way you avoid submitting your entire page.
In case you're not familiar with the details, here's a good overview on getting started with Ajax and MVC 4
http://ofps.oreilly.com/titles/9781449320317/ch_AJAX.html
I have never done a POST request via Razor and MVC4. I think i have the core methods and stuff down but i am having difficulty fulfilling an actual POST request.
Here is the Razor View page code...
#model UserJob
#Html.HiddenFor(Model => Model.UserCode)
#Html.DropDownList("jobCode")
<input type="submit" value="Add" class="btn btn-default" />
And the method which i want to fulfil the POST method is.....
[HttpPost]
public ActionResult AddSkill(UserJob model)
{
db.UserJobs.Add(model);
db.SaveChanges();
return RedirectToAction("Jobs", new { UserCode = model.UserCode });
}
You Razor view needs to have the form. Either use #Html.BeginForm(...) to enclose your inputs, or just write HTML form markup yourself.