Form submmited twice from MVC Controller Action - c#

I am trying to explain why a user was able to submit the same form details twice. At first, I was thinking that the submit button was pushed twice, this may still be the case.
When I checked the results in the database, I can see the same information has been entered twice, but also the same datetime stamp has been entered, down to the second. Surely it takes at least another second to push submit again if this is the case.
In addition after the survey is inputted and saved the user is redirected to a different page.
Why does this happen?
[HttpPost]
public ActionResult InputResult(SurveyViewModel model)
{
if (ModelState.IsValid)
{
Survey_Result InputResult = new Survey_Result();
InputResult.SurveyStatusID = model.SurveyStatusID;
InputResult.Q1DateCompleted = DateTime.Now;
InputResult.Q2 = model.Q2;
InputResult.Q3 = model.Q3;
InputResult.Q10 = model.Q10;
InputResult.Q11 = model.Q11;
InputResult.Q11Other = model.Q11Other;
InputResult.DateAdded = DateTime.Now;
InputResult.AddedBy = Convert.ToInt32(User.Identity.GetUserId());
_surveyService.AddSurvey(InputResult);
_surveyService.Save();
return RedirectToAction("Details", "Survey", new { id = model.SurveyStatusID, feedback = "InputComplete" });
}
return RedirectToAction("Details", "Survey", new { id = model.SurveyStatusID, feedback = "InputError" });
}

The code looks fine to me. If you have access to the user, you could pop Fiddler on it to see if it is posting data twice. If it doesn't happen all the time then its almost certainly user error IMHO.
If you don't have access to the client you could pop in a log entry on each post request or debug line if you are in a position to collect it on this server.
I had similar issues and client side javascript to disable the button on click did the trick for me.

Are you sure you need both AddSurvey and Save?
Remove save and retry.

Related

stripe SessionCreateOptions not persisting client_reference_id that is passed to it

Just migrated to Stripe.com. I am creating a checkout session programmatically. See code snippet below. When I test, the User.Identity.GetUserId() comes back with a value and it is sent to stripe. However, when end user completes the payment, Stripe.com is not sending back the client_reference_id (it is null) in the event checkout.session.completed that I am listening to.
I get back my client_reference_id when I do payment links (send via querystring)
What am I doing wrong?
[HttpPost]
[AllowAnonymous]
public ActionResult SendToCheckout(ProcessPaymentViewModel model)
{
StripeConfiguration.ApiKey = _apiSecret;
var options = new SessionCreateOptions
{
ClientReferenceId = User.Identity.GetUserId(),
SuccessUrl = ConfigurationManager.AppSettings["BaseUrl"] + "/PaymentComplete",
CancelUrl = ConfigurationManager.AppSettings["BaseUrl"] + "/Subscribe",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
Price = model.PriceId,
Quantity =long.Parse(model.Quantity)
},
},
Mode = "payment",
};
var service = new SessionService();
var session = service.Create(options);
return Redirect(session.Url);
}
reviewed stripe.com documentation. It appears I am setting it correctly. The other questions posted is one is really not answered and the other one says it should be in that webhook event. I dumped the values and it should client_reference_id: null
The code you shared looks correct and it's almost certain that you are not setting a value when you think you are.
The best path forward is to hardcode a value in your code and you should see that it works as expected and that the problem is the value you put in. What I would do is hardcode AAAA, confirm it's there, and then concat AAAA and the value in your variable and another string like AAAA-<userid>-BBBB and see that you get AAAA--BBBB because your string is null or empty.
This isn't a Stripe bug, that feature works as expected and is used widely but I've tested it quickly to confirm.
You can also look at the response on the Session creation after your code and just print session.ClientReferenceId and see that it's null right now.

How to get the PK of a new item created in the database to use right away and update it's settings

I am using a web API with ASP.NET Core MVC, entityframework and angular as the front-end.
In my angular application I have Stepper component https://material.angular.io/components/stepper
In the first step I want to fill out my form and as soon as I click next I want to create the task and on the second form I want to update the settings for that newly created task. However, I need to get the PK of the newly created task to update the settings on my second form.
There is an api call to create a new task in the tTask table in sqlserver.
This is my API Post Method.
[ResponseType(typeof(CreatingTaskModel))]
public IHttpActionResult PosttTask(CreatingTaskModel CreatingTaskModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var newtTask= new tTask
{
TaskName = CreatingTaskModel.TaskName,
TaskDescription = CreatingTaskModel.TaskDescription,
EmailSubject = CreatingTaskModel.EmailSubject,
EmailBody = CreatingTaskModel.EmailBody,
FK_TaskTeam = CreatingTaskModel.tTaskTeam.pk_TaskTeam,
Enabled = CreatingTaskModel.Enabled ? 1:0,
};
db.tTasks.Add(newtTask);
db.SaveChanges();
var pk = new {id = CreatingTaskModel.PK_Task};
return Ok("Success");
}
This is the submit code in angular :
onSubmit()
{
if(this.taskForm.valid)
{
this.service.createTask(this.taskForm.value).subscribe((data) => {
debugger;
console.log(data)
})
}
}
I saw that return Ok() in my webapi returns a message and I was thinking of sorting the tTask table by decending order of after the db.SaveChanges();
and returning the last item that it find and sending it back in the Ok(pk) and then casting that into an integer on my client-side and using that to get the data to update it.
What is the correct way to do this? Should it be done in sql or on the webapi?
This is what I ended up doing:
var newObject = new
{
id = newtTask.PK_Task,
message = "Success"
};
return Ok(newObject);
and on angular I have this:
onSubmit(){
if(this.taskForm.valid) {
this.service.createTask(this.taskForm.value).subscribe((data) => {
if(data.message){
if(data.message == "Success"){
this.getRecipentData(data.id);
this.AdditionalRecipientForm.controls['FK_Task'].setValue(data.id);
this.pk_Task = data.id;
}
}
debugger;
console.log(data)
})
}
It just doesn't seem practical to do this, but it does the job. What do you guys think? Should I maybe instead of going to the serve twice maybe after it goes to the done filling out both forms submit them both? Like call create method in my API and then call my second API to update the data the was submitted in the second form. I am just looking for ideas or most common practice for these types of situations.
After you've added it to the database and called db.SaveChanges, the key will be assigned to the object. So, after db.SaveChanges, you should just be able to simply reference newtTask.Id.
So I assume you manually assign the id of the new task via PK_Task = CreatingTaskModel.PK_Task, and that it's indeed this id that gets saved to the Db. Therefore the id column should not be autoincrementing.
If that's true you can, as Louis said before, simply return your object or only the id if you're concerned about bandwidth.
You can do return new OkObjectResult(pk); or just use the shorthand: return Ok(pk);, which will also return an OkObjectResult with the pk object as payload.

C# MVC ActionLink clear session before reloading page

I have a simple form page, where user can fill some data. After I press post, I need all that data to remain as it is, so if user wants to change data, he/she can. After I save data I store Client object in Session and every time I press Save button, I check if there is user already in Session.
Now I have #Html.ActionLink("New client", "NewUser");, that I press, when I want to create new user. So this link would reload the page and clear that Session.
Note that "New user" should redirect to Index instead, but I managed to get it to work like that, but is not valid way to do so.
Controller code:
public ActionResult Index()
{
return View(_vm);
}
public ActionResult NewUser()
{
Session["newClient"] = null;
return RedirectToAction("Index");
}
clearing the session can only done on backend so you have to make action to clear the session but you dont need return RedirectToAction("Index"); instead return the view
public ActionResult NewUser()
{
Session["newClient"] = null;
return View("Index",_vm);
}
since you are redirecting to the index view for creating new user

Cookie is null in rare cases after redirecting to ACS and back

On my website, there is a registration form. After having filled this in, the user gets redirected to Azure ACS in order to log in. After having logged in, the user gets redirected back to my website and is to be registered and logged in.
The registration form is submitted by a JavaScript. The information that the user has filled in is saved to a cookie by the RedirectToProvider method in the RegisterController and the user is redirected to ACS. When the user has been redirected back to the website from ACS, the cookie is then read by the RegisterUser method in the RegisterController. The problem is: this works 95% of the time. 5% of the time, the cookie is null when the user comes back. I have been unable to track the cause of this and am wondering if there are any known issues or something that I may have overseen. The form code looks like this:
#using (Html.BeginForm("RedirectToProvider", "Register", FormMethod.Post, new { id = "registerForm" }))
... various fields...
<input type="button" class="btn" id="registerSubmitButton" value="Register" onclick="RegisterRedirect()" />
}
The RegisterRedirect() JavaScript that submits the form (with irrelevant functionality left out here):
var RegisterRedirect = function () {
$("#registerForm").valid();
$("#registerForm").submit();
}
The RedirectToProvider method in the RegisterController:
[AllowAnonymous]
[HttpPost]
public ActionResult RedirectToProvider(RegisterViewModel viewModel)
{
if (ModelState.IsValid)
{
var providerUrl = viewModel.SelectedProviderUrl;
viewModel.SelectedProviderUrl = "";
var json = JsonConvert.SerializeObject(viewModel);
try
{
var cookie = new HttpCookie("RegisterViewModel", json)
{
Expires = DateTime.Now.AddMinutes(10)
};
ControllerContext.HttpContext.Response.Cookies.Add(cookie);
}
catch (FormatException)
{
return RedirectToAction("Index", "Error", new { reason = "Cookie saving error." });
}
return Redirect(providerUrl);
}
return RedirectToAction("Index", "Error", new { reason = "Invalid data. Try again." });
}
The user is redirected to ACS and chooses to log in with, for example, Gmail. ACS calls back to my ClaimsAuthenticationManager (configured in web.config). Afterwards, the method to be called back to (configured in ACS) is called and in turn calls the RegisterUser method that is supposed to read the cookie:
[Authorize]
public ActionResult RegisterUser(User user){
var cookie = ControllerContext.HttpContext.Request.Cookies["RegisterViewModel"];
if (cookie != null){
... registers the user...
}
}
95% of the time, the cookie is not null. 5% of the time, something fails and the cookie is null. The fail rate is higher during the first builds of the website after the Azure Emulator has just started, and lower later on. I have read that it could have something to do with sessions. Does anyone see an obvious error or have any advice? Thanks in advance for any help!
I think that the problem is due to the fact that you sometimes get redirected to a different web role instance where the cookie you created is missing.

Prevent back button

I am using Razor on ASP.NET MVC with C#.
I am calling an external web page to process a credit card and it returns to me. I then display a receipt.
I'd like to prevent them from going back to the previous screen.
I do not have an underlying cs page, like asp since these are .cshtml files, to grab the event.
This receipt page is a View so I cannot put JavaScript in the header since it would affect every page using it.
Anyone know how I prevent the back button in this circumstance?
One possibility is to exclude the page you don't want to get back to from caching on the client. This could be done by setting the proper response headers. Here's an example with a [NoCache] custom action filter which you could use to decorate the corresponding controller action.
Firstly, if the previous page posted data to the server, best to Redirect(...) to another action after the successful processing, to avoid the data being resubmitted on "Refresh".
Then also set the page to expire, so the back button doesn't work:
https://stackoverflow.com/a/5352953/864763
You're asking the wrong question. Don't try to disable "back" on the client. This will be doomed to fail; you may be able to make it harder, but you'll never win that fight. Instead you should re-write the particular page that you have such that it will only ever process the credit card once. You should (on the server) "remember" that you've processed the credit card so that if the user goes back to the page to resubmit it you can just give them an error message saying "you have already submitted this information, you cannot submit this request twice".
Now, there are several ways of accomplishing this general goal, and some are better than others, but that's the goal you need to strive towards.
One way to do this is to go to every page that will redirect the user to this credit card form; just before submitting the request add something to that user's session (i.e. "pendingCreditCardSubmission" = true) Once they submit that request you then check for that session variable. If it's true, submit the request and set it to false, if it's false or not there then send an error message to the user.
This is how we did it:
public class NoBackFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.ExpiresAbsolute = DateTime.Now;
filterContext.HttpContext.Response.Expires = 0;
filterContext.HttpContext.Response.CacheControl = "no-cache";
filterContext.HttpContext.Response.Buffer = true;
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow);
filterContext.HttpContext.Response.Cache.SetNoStore();
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
if (!filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.HttpContext.Request.HttpMethod != "POST" && !filterContext.Controller.ControllerContext.IsChildAction)
{
var after = filterContext.HttpContext.Request.RawUrl;
var session = GetSession(filterContext);
if (session["Current"] != null)
{
if (session["Before"] != null && session["Before"].ToString() == after)
filterContext.HttpContext.Response.Redirect(session["Current"].ToString());
else
{
session["Before"] = session["Current"];
session["Current"] = after;
}
}
else
{
session["Current"] = after;
}
}
base.OnActionExecuting(filterContext);
}
private HttpSessionStateBase GetSession(ActionExecutingContext context)
{
return context.HttpContext.Session;
}
}
After this you can implement it either in the general scope or in the controller scope.
It has been long since this was asked, but my fix was adding the [NoCache] above the WebPageController class.
[NoCache]
public class WebPageController : Controller
{
public JsonResult JsonError(Exception exception)
{
if (exception == null) throw new ArgumentNullException("exception");
Response.StatusCode = 500;
return new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
success = false,
message = exception.Message,
detail = exception.ToString()
}
};
}
in MVC aspnet framework, you may choose to RedirectToActionPermanent
Which then it tells the browser 301 response code.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.redirecttoactionpermanent?view=aspnetcore-5.0

Categories

Resources