making an Ajax call on 'Save' button click on popup - asp.net-mvc-3

I am working on a form where the user clicks on Register button, then a popup opens for the user to enter the details for registering....The user name should be unique for this, so when the user clicks on the submit button im checking with the database whether that username is already existing or not, if it already exists the im showing the error message on the same popup that 'username already exists'...
This is my code for that which gets fired on the save button click
$.ajax({
url: '#Url.Content("~/Registration/CheckUNameExists")',
type: 'GET',
data: { UName: $('#UserName').val() },
success: function (data) {
if (data == 'True') {
$('#UserAddError').html('User Name already exists!');
} else {
$("#update-message").html('');
$("#addUserForm").submit();
$(this).dialog("close");
}
}
});
public JsonResult CheckUNameExists(string UName)
{
}
[HttpPost]
public ActionResult RegisterUser(Regitration Reg)
{
}
This is working fine, but in some rare cases this is giving a problem. After filling the details in the popup when the user clicks the save button some times its taking 3-4 seconds time for it to save and in that case the users are clicking the save button 2-3 times...In that case the save button click is checking the method CheckUNameExists on for the 1st time and then RegisterUser is being called multiple times based on the number of times the user clicks the save button nd the same records are getting saved into db multiple times..
For testing purposes i put a breakpoint so that i can click the 'save' button n number of times, then clicked the save button 4 times and CheckUNameExists was hit only the 1st time but after that RegisterUser was called 4 times and the record was inserted 4 times.
Don't understand the issue here and how to get rid of it?

You are sending a GET ajax request to check if the user exists, so the respone is probably getting cached by the browser. jQuery provides a setting for the ajax function that you can use for enabling/disabling caching which is enabled by default, see the cache setting here
Try disabling caching for that ajax call, adding the option cache:false to the ajax settings:
$.ajax({
url: '#Url.Content("~/Registration/CheckUNameExists")',
type: 'GET',
data: { UName: $('#UserName').val() },
cache: false,
success: function (data) {
...
That should fix the issue of CheckUNameExists not being called.
However you can still have race conditions with multiple ajax calls. A better solution would be to send a single ajax POST that does both the checking of the unique name and the user registration. This should prevent errors due to the asynchronous nature of ajax calls, when your GET for veryfing the user happens before the first time the button was clicked finishes submitting the form.
This will require a bit of refactoring:
$(".actionSubmit").click(function () {
$(".actionSubmit").attr('disabled', 'disabled');
$.ajax({
url: '#Url.Content("~/Account/RegisterUser")',
type: 'POST',
data: $("#addUserForm").serializeArray(),
success: function (data) {
if (!data.success) {
$('#UserAddError').html(data.errorMessage);
$(".actionSubmit").removeAttr('disabled');
} else {
$(this).dialog("close");
//Possibly redirect here, same logic you had in RegisterUser method
}
}
});
return false;
});
[HttpPost]
public ActionResult RegisterUser(Regitration Reg)
{
//Check if user exists, whatever you were doing in CheckUNameExists
bool userAlreadyExists = ...;
if (userAlreadyExists)
{
return Json(new { success = false, errorMessage = "User Name already exists!" });
}
//Register user
...
return Json(new { success = true });
}
You may want to combine that solution with JavaScript that disables the button on its click event and does not enables it again until you are done (either because the name already exists or because the POST finished). I have showed this in the code above, where I am disabling/enabling the submit button.
The changes above should prevent issues related with requests coming from the same client. However you can still have users from different machines trying to register at the same time with the same user name. To prevent those errors you will need to wrap the C# code in a transaction scope, so both user verification and registration happen inside a transaction:
[HttpPost]
public ActionResult RegisterUser(LoginModel model)
{
using (var tran = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
//Check if user exists, whatever you were doing in CheckUNameExists
bool userAlreadyExists = ...;
if (userAlreadyExists)
{
return Json(new { success = false, errorMessage = "User Name already exists!" });
}
//Register user
...
tran.Complete();
}
return Json(new { success = true });
}
Hope this helps!

Related

My behaviourSubject is returning null on load of a page in angular 9, it is not updating with current value

Onlogout() I am making the userSubject.next(null). So while logging in it is taking the null value first and only after refresh of the page it is taking the updated user value.
please can anyone suggest if I am missing anything, or the best way to update the behaviourSubject on load of the page. please find the code below.
/* In login Service*/
//in the constructor
constructor(private http: HttpClient, private router: Router) {
this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
this.user = this.userSubject.asObservable();
}
public get userValue(): User {
return this.userSubject.value;
}
//in login method to get user details
login(username, password) {
return this.http.post<User>(`${environment.apiUrl}/v1/login`, { username, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
console.log("inisde usersubjec====>", this.userSubject);
this.userSubject.next(user);
return user;
}));
}
//logout method
logOutUser() {
// remove user from local storage to log user out
localStorage.removeItem('user');
return this.http.post(`${environment.apiUrl}/tag/v1/logout`, null).subscribe((res) => {
console.log("inside logOutUser method:UserValue==>", this.user);
this.userSubject.next(null);
this.router.navigate(['/login']);
}
);}
/*In login component ts */
onSubmit() {
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
};
this.loginService.login(this.getformControls().username.value, this.getformControls().password.value)
.subscribe(
data => {
console.log("User Logged in Data==>", data);
this.router.navigate(['/homepage']);
}
);
}
So after post call, getting the user details and updates the behaviourSubject and then navigate to homepage. But on landing to homepage the userValue is being null and not the updated value. Only after refreshing the homepage it is getting the updated value. Please suggest how can I get the update uservalue in homepage on landing itself without refreshing the page.
in the constructor try to make the JSON.parse more safe as the getItem might be null at the beginning, second thing try to use this.user and use async with it in the template and it will give you the latest value from the BehaviorSubject, .value from BehaviorSubject doesn't give you the latest emitted value.
I recommend you if you can provide a stackblitz it will be great.

Do I need to verify the result for Google Invisible reCaptcha

I am following the instructions on this page
to implement the invisible recaptcha. Everything works great, but how do I know it is working? Is there a way to force a false to test it?
Also, The documentation is not clear on the above page, but some places have additional code to verify the users "response" (it's invisible so i'm not sure what the response is here) - so do I need to add additional back end logic to hit this endpoint with the invisible reCaptcha resulting token and my secret key?
What happens when the user clicks submit on the invisible recaptcha? What is done in the API to return the token? What is the token for? What does the siteverify api then do to determine its a person? Why isnt additional verification needed when the reCAPTCHA V2 (visible click one) is used?
After some testing it looks like you could just do the front end part. The data callback function is not called until google is sure you are a person, if google is not sure then it loads the "select which tiles have a thing in them" reCaptcha to be sure. Once the reCaptcha api is sure that it is a person, the data callback function is fired - at that time you can do further validation to ensure that the token you received during the callback is the one that google actually sent and not a bot trying to fool you by hitting your callback funct - so from there you do server side processing for further validation. Below is an example of a C# ashx handler - and ajax for the validation
function onTestSubmit(token) {
$.ajax({
type: "POST",
url: "testHandler.ashx",
data: { token: token },
success: function (response) {
if (response == "True") {
//do stuff to submit form
}
}
});
}
And the ashx
public class testHandler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
string token = context.Request.Form["token"];
bool isCaptchaValid = ReCaptcha.Validate(token);
context.Response.Write(isCaptchaValid.ToString());
}
public bool IsReusable {
get {
return false;
}
}
}
public class ReCaptcha
{
private static string URL =
"https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}";
private static string SECRET = "shhhhhhhhhhhhhhSecretTOken";
public bool Success { get; set; }
public List<string> ErrorCodes { get; set; }
public static bool Validate(string encodedResponse)
{
if (string.IsNullOrEmpty(encodedResponse)) return false;
var client = new System.Net.WebClient();
var googleReply = client.DownloadString(string.Format(URL, SECRET, encodedResponse));
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);
return reCaptcha.Success;
}
}
Yes, you do.
You need to understand that invisible reCaptcha is a process with multiple steps, all of which end up providing a final response regarding the humanity of the user.
In simple words, when the users submits a form (or does whatever it is you are trying to keep bots away from with Invisible reCaptcha) you'll be sending your public sitekey to your backend, which will fire up a verification payload to Google.
In my very basic example, this is the button which the hopefully human visitor clicks to submit a form on my site:
<button type="submit" class="g-recaptcha" data-sitekey="xxxxxxxx_obscured_xxxxxxxx" data-callback="onSubmit">Submit Form</button>
Note how the the button has the data-callback "onSubmit", which upon submission runs this small script:
<script type="text/javascript">
var onSubmit = function(response) {
document.getElementById("simpleForm").submit(); // send response to your backend service
};
</script>
The backend service in my example is a vanilla PHP script intended to process the form input and store it on a database and here comes the tricky part. As part of the POST to the backend, besides the form fields the user filled is the response from the service (and since you may or may not be doing a lot of things on the front-end where the user could manipulate the response before it's posted to your backend, Google's response is not explicit at this point)
On your backend, you'll need to take g-recaptcha-response that came from google and post it to a verification API using your private key (which is not the one on the form) in order to get the human/robot verdict which you can act upon. Here's a simple example written in PHP and hitting the API with cURL:
$recaptcha_response = $_POST["g-recaptcha-response"];
$api_url = 'https://www.google.com/recaptcha/api/siteverify';
$api_secret = 'zzzzzzz_OBSCURED_SECRET_KEY_zzzzzzzzzzz';
$remoteip = '';
$data = array('secret' => $api_secret, 'response' => $recaptcha_response);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($api_url, false, $context);
$captcha_response = json_decode($result, true);
// at this point I have the definite verdict from google. Should I keep processing the form?.
if ($captcha_response['success'] == true) {
// I heart you, human. Keep going
$captcha_error = 0;
}
else {
// Damn robot, die a slow and painful death
$captcha_error = 1;
}
I make a final decision based on $captcha_error (basically 1 means halt, 0 means keep processing)
If you rely solely on getting the g-recaptcha-response you're having Google do the work and then ignoring the result

publisher initiates twice, one proper one only to self

for some reason my publisher initiates twice when I create a new a new session. However the 2nd one, isn't in the div where it's supposed to be. Also if you connect to the session you'll get the same so it only show for yourself.
I'm trying to find out why it's appearing. Here's some snippets:
var getApiAndToken, initializeSession;
​
getApiAndToken = function() {
var apiKey, customer_id, sessionId, token;
if (gon) {
apiKey = gon.api_key;
}
if (gon) {
sessionId = gon.session_id;
}
if (gon) {
token = gon.token;
}
if (gon) {
customer_id = gon.customer_id;
}
initializeSession();
};
​
initializeSession = function() {
var publishStream, session;
session = OT.initSession(apiKey, sessionId);
session.connect(token, function(error) {
if (!error) {
session.publish(publishStream(true));
layout();
} else {
console.log('There was an error connecting to the session', error.code, error.message);
}
});
$('#audioInputDevices').change(function() {
publishStream(false);
});
$('#videoInputDevices').change(function() {
publishStream(false);
});
return publishStream = function(loadDevices) {
var publisherOptions;
publisherOptions = {
audioSource: $('#audioInputDevices').val() || 0,
videoSource: $('#videoInputDevices').val() || 0
};
OT.initPublisher('publisherContainer', publisherOptions, function(error) {
if (error) {
console.log(error);
} else {
if (loadDevices) {
OT.getDevices(function(error, devices) {
var audioInputDevices, videoInputDevices;
audioInputDevices = devices.filter(function(element) {
return element.kind === 'audioInput';
});
videoInputDevices = devices.filter(function(element) {
return element.kind === 'videoInput';
});
$.each(audioInputDevices, function() {
$('#audioInputDevices').append($('<option></option>').val(this['deviceId']).html(this['label']));
});
$.each(videoInputDevices, function() {
$('#videoInputDevices').append($('<option></option>').val(this['deviceId']).html(this['label']));
});
});
}
}
});
};
};
it also asks me for device access twice.
I see two general problems in the code you provided:
The variables api_key, session_id, and token inside the getApiAndToken() function are scoped to only that function, and therefore not visible inside initializeSession() where you try to use them.
The goal of the publishStream() function is not clear and its use is not consistent. Each time you invoke it (once the session connects and each time the dropdown value changes) this function creates a new Publisher. It also does not return anything, so when using it in the expression session.publish(publishStream(true)), you are effectively just calling session.publish() which results in a new Publisher being added to the end of the page because there is no element ID specified. That last part is the reason why you said its not in the <div> where its supposed to be.
It sounds like what you want is a Publisher with a dropdown to select which devices its using. I created an example of this for you: https://jsbin.com/sujufog/11/edit?html,js,output.
Briefly, the following is how it works. It first initializes a dummy publisher so that the browser can prompt the user for permission to use the camera and microphone. This is necessary for reading the available devices. Note that if you use a page served over HTTPS, browsers such as Chrome will remember the permissions you allowed on that domain earlier and will not have to prompt the user again. Therefore on Chrome, the dummy publisher doesn't cause any prompt be shown for a user who has already run the application. Next, the dummy publisher is thrown away, and OT.getDevices() is called to read the available devices and populate the dropdown menu. While this is happening, the session would have also connected, and on every change of the selection in either of the dropdowns, the publish() function is called. In that function, if a previous publisher existed, it is first removed, and then a new publisher is created with the devices that are currently selected. Then that new publisher is passed into session.publish().

Angular $http returning new values only once

I am new to Angular, and set up a simple example with a REST Api config in Codeigniter that returns a json (default) thread list. No problems!
Until, I add an update to the Database. If I clear/then call getThreads again, I receive the same list of items. A page refresh solves this. I can see in firebug that its only calling the url:api/example/threadlist/id/'x' once per page load.
function ThreadsCtrl($scope, $http, $templateCache) {
$scope.getThreads = function(id) {
if (!id) { id = 'reset'; }
$http({method: 'GET', url: 'api/example/threadlist/id/' + id, cache: $templateCache}).
success(function(data) {
$scope.threadslist = data; //set view model
}).
error(function(data) {
$scope.threadslist = data || "Request failed";
});
};
How would I make it so that it always calls a new list of data rather than reuses the old.
Thanks!
If i understood your question correctly your ajax call is being cached so you have to remove cache:$templatecache from your code

Should i use threads when executing action method through AJAX?

I am building a questionnarie. When a user clicks on an answer possibility for a multiple choice question (this is a radio button), i call an action method to save this answer.
The code:
<script language="javascript">
$(document).ready(function () {
$('.MCQRadio').click(function () {
var question_id = $(this).attr('question-id');
var mcq_id = $(this).attr('mcq-id');
$.ajax({
url: '/SaveSurveyAnswers/SaveMCQAnswer',
data: { "mcq_id": mcq_id, "question_id": question_id },
success: function (data) {
}
});
});
});
The code to save the answer:
public EmptyResult SaveMCQAnswer(int mcq_id, int question_id)
{
MCQ_Answers mcqa = null;
try
{
mcqa = db.MCQ_Answers.Single(x => x.question_ID == question_id);
}
catch (InvalidOperationException e)
{
}
if (mcqa != null)
{
mcqa.mcq_id = mcq_id;
}
else
{
MCQ_Answers mcq_answer = new MCQ_Answers()
{
question_ID = question_id,
respondent_id = 1
};
db.MCQ_Answers.AddObject(mcq_answer);
}
db.SaveChanges();
return new EmptyResult();
}
If a question has 5 answer possibilities, and i click on them randomly and fast, and then go back to the previous page, ie, when i return the correct answer wont be saved. Should i use threading to make sure the correct answer is saved? And how?
Thanks
rather than saving your answer by post all the time, you can just create a JSOn object and save the answers within json. you can then at the end post all completed answers in one go.
take a look at this: http://msdn.microsoft.com/en-us/scriptjunkie/ff962533
basically this will allow you to store session data - json on the remote machine you then just need an add, delete function and away you go....
i use this to huge extent in an application that would require the server to be updated with the location of objects on a canvas, however with sessvars i just keep all the X and Y locations within there and do a final push of JSON when i am done.
if you change pages, you can then get your values from the JSON object without a server call.
as a note you may also be better off with tabs or hiden sections of form, and therfor reduce the need to re-populate say page1, page2 etc as they will already be there, just hidden!

Resources