Async Web API and Ajax - asp.net-web-api

I have a long running job that is started from the admins web page.
The Job can only be started once, and therefore I have a singleton, that also holds the different state messages of the job.
After the admin has started the job (Ajax call works) a timer in javascript gets started, that should trigger checks of the state of the job every 10 seconds (the timer works, the calls get triggered).
My Problem is, that these calls never reach the server, until the Job is finished ... After the job has finished, all the status calls (that should have been async) are processed..
WEB API CODE:
public class CleanUpServiceToolController : ApiController
{
[HttpPost]
public async Task StartJob(StartCondition startCondition)
{
if (CleanUpServiceTool.Instance.Status == "Neu")
{
CleanUpServiceTool.Instance.Status = "I'm Busy";
await Task.Delay(60*1000);//CleanUpServiceTool.Instance.Start(startCondition);
CleanUpServiceTool.Instance.Status = "Neu";
}
}
public string GetStatus()
{
return CleanUpServiceTool.Instance.Status;;
}
}
JAVASCRIPT the javascripts are logically splitted.. but all calls go through this code
return $.ajax({
url: url
type: type,
data: (type === "PUT" || type === "POST") ? JSON.stringify(input) : input,
contentType: contentType,
dataType: dataType,
jsonpCallback: jsonpCallbackFunctionName,
timeout: timeout,
async: true,
cache: false
}).done(function (response) {
var output = dataType === "jsonp" ? response : JSON.parse(response, true);
successCallback(output);
}).fail(function (response) {
var output;
try {
output = JSON.parse(response.responseText, false);
} catch (exception) {
output = response.responseText;
}
errorCallback(response.status, response.statusText, output);
}).always(function () {
if (alwaysCallback) {
alwaysCallback();
}
});
My code makes something like this:
get status of the job/ instance (Works)
if status is "Neu" then enable the "Start Job" Button (works)
on click send ajax POST data to the server (works, server starts the Job)
Start a timer and Ask for job status (Does NOT work: The calls get stacked and only get called when the StartJob Task is finished...
To test the javascript logic I used Task.Delay, I don't want to start up the job each time during testing.
I really don't know why my "async" calls are actualy "sync" calls!
I even tried making the GetStatus async , which really doesn't make much sence, (opening a thread just to read the Property of my instance)
Any Ideas, suggestions, fixes?
Thanks

Related

Django render template on AJAX success

I am trying to make a web application based on Django that takes user input and performs Heavy background task that completes in almost five to ten minutes. When the background task is completed, few parameters are supplied to the template to render. Everything works fine and the page loads after that.
But when I am trying to use AJAX for this as it does'nt seems good that the page is loading for so long due to background heavy processing, I am not able to figure out how to reload the page (Though I am able to show an alert on completion but instead of this I want to re-render the page)
Here is my views.py code:
def index(request):
#All Background process code goes here
return render(request, 'form.html', {'scanResults' : scanResults, 'context_list' : context_list, 'scanSummary' : scanSummary})
Here is my AJAX call
<script type="text/javascript">
$(document).on('submit','#scanForm', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '/scanner/',
data: {
email: $('#email').val(),
context: $('#context').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
},
success:function(response){
alert('Scan Completed');
location.reload();
}
});
});
I am not able to figure out, what should I write in success function to reload the page that index function has returned to template.
My main motive is to show a progress bar that tells the progress of process in background (I have'nt implemented the code yet )and once the process is completed , refresh the page with response.
Thank You
If you want to check the progress of a process you may need a polling mechanism
as a solution.
This requires you to have a Model that has a state to determine if your scan
is still in progress or has succeeded.
Since you will reload the page to display the results, you should have
a logic in your index view to return a different template or context
for when a user has yet to start scanning and when the scanning is successful.
from django.http import JsonResponse
def index(request):
if status == 'success':
# `status` may come from a Model which has a state .
# If `status` is 'success' this means that your scanning has
# finished so you can have a different page or update context_list
# based on success data.
# Display input form
form = scannerForm()
return render(request, 'form.html', {
'form': form,
'context_list' : context_list,
'scanSummary' : scanSummary
})
You need a view to continuously check the scan status and returns a JSON response.
def scanner(request):
#All Background process code goes here
form = scannerForm(request.POST)
status = form.perform_task()
# During the task, your Model state should also be
# updated and return the status whether it is success, pending or failed etc..
return JsonResponse({
'status': status,
})
Run the ajax poll to check the scanner view.
<script type="text/javascript">
$(document).on('submit','#scanForm', function(e){
e.preventDefault();
checkScanStatus();
});
function checkScanStatus () {
$.ajax({
type: 'POST',
url: '/scanner/',
data: {
email: $('#email').val(),
context: $('#context').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
},
success: handleCheckScanStatus,
error: handleScanError
});
}
function handleCheckScanStatus (result) {
if (result.status == 'success') {
// Reload the page and display the condition you set for 'success' state
// on the `index` view.
location.reload();
} else {
// Show progress bar indicating that the process running in the background
const interval = 5000; // Five seconds
window.setTimeout(checkScanStatus, interval);
}
}
function handleScanError (response) {
console.error(response)
}
</script>
I would suggest to look into django celery for async tasks and django-fsm for transitioning model states.
If you just want a simple loader and do not need the check the specific status of your background task, you can use jQuery AJAX's beforeSend method to display a progress bar until the AJAX request finishes.

Spring MVC, Rest Ajax Call and Session Scope Objects

I want to solve following issue. I have a Spring-MVC Application with Thymeleaf, with a post request (sent by a form) I trigger a simulation task, what could take several minutes. The task process big number of data and we would like to have a progress bar via JavaScript. If there are two sessions, the simulation should be triggered independently and each browser shows its progress status.
Currently we have a solution, what is not really working well all the time.
The MVC Controller gets the Post request:
#Autowired SimulatorView view; // SESSION SCOPE
#PostMapping("/view")
public String run(#ModelAttribute(CHECKS) ChecksDto checksWrapper, Model model) throws InterruptedException, ExecutionException {
view.setStatisticDto(simulate(checksWrapper)); // Can take several minutes
return "simulation/result :: simulated";
}
When I trigger the simulation on my WebGUI, a progress bar has been displayed and via JavaScript I am calling Rest Methods frequently to ask for the status of the progress.
RestController
#RequestMapping("simulation/api")
public class SimulatorApi {
#Autowired SimulatorView view; // SESSION SCOPE
#RequestMapping("/progressStream")
public double progressStream() {
return view.getProgress().progressStream();
}
#RequestMapping("/progressInvoice")
public double progressInvoice() {
return view.getProgress().progressInvoice();
}
}
My JavaScript code snippet looks like:
function registerSimulationRunEvent() {
// this is the id of the form
$("#simulatorForm").submit(function(e) {
handleSimulationStarted();
var url = location.protocol + "//" + location.host + "/fdsclient/simulation/view";
$.ajax({
type: "POST",
url: url,
data: $("#simulatorForm").serialize(), // serializes the form's elements.
success: function(data) { handleSimulationFinished(); },
error: function(xhr, error) { handleSimulationError(); }
});
e.preventDefault(); // avoid to execute the actual submit of the form.
});
}
function handleSimulationStarted() {
replaceResultPanelRunning(); // THYMELEAF FRAGMENT EXCHANGE
}
function handleSimulationFinished() {
stopResultPanelAnimation(); // STOP PROGRESS BAR ANIMATION
replaceResultPanelSimulated(); // EXCHANGE THYMELEAF FRAGMENT
}
function handleSimulationError() {
stopResultPanelAnimation();
replaceResultPanelError();
}
function replaceResultPanelRunning() {
var url = // URL;
$("#resultDiv").load(url);
startResultPanelAnimation();
}
// ANIMATION
var animationInterval = null;
function startResultPanelAnimation() {
animationInterval = setInterval(animateResultPanel,4000);
}
function stopResultPanelAnimation() {
clearInterval(animationInterval); // stop the interval
}
function animateResultPanel() {
$("#simulatorProgressLabel").animate({opacity: '0.4'}, "slow");
$("#simulatorProgressLabel").animate({opacity: '1.0'}, "slow");
}
I know using session scope for rest services is a bad thing, but I didn`t know yet what is a good and easy solution. On the other hand currently different browser can simulate independently, but not always the progress bar works (especially when trigger first time mostly doesnt work). The IE11 only works when the Developer Tools are activated. When deactivating the tool while progress, the progress bar stops to grow.
What I would like to know is, how a good solution looks like when using template engine with Spring-MVC and Thymeleaf for triggering the process and displaying the status of progress via Javascript (as JQUery). Thank you in advance.
I have done a similar thing using Jquery AJAX POST submission. You can do something like this. This will submit POST request as a JSON format to the controller and wait for a response. A progress UI component can be shown during this waiting period.
//Start Progress display
function setStatistic(){
var data = JSON.stringify(//build your ChecksDto)
if (data) {
$.ajax({
url : '/view',
headers : {
'Content-Type' : 'application/json'
},
method : 'POST',
dataType : 'json',
data : data,
success : function(data) {
if (data.status == 200) {
// Stop Progress display
// Handle success status
}
},
error : function(xhr, status, error) {
// Stop Progress display
// Handle errors here
}
});
}
}
You also need to change Controller method to retrieve ajax requests as follows,
#ResponseBody
#PostMapping("/view")
public String run(#RequestBody ChecksDto checksWrapper, Model model) throws InterruptedException, ExecutionException
At least I found the solution in another Stackoverflow Page. The magic word is setting ajax cache to false.
$.ajaxSetup ({
// Disable caching of AJAX responses */
cache: false
});

async ajax call - show loading gif

I'm using ajax to call a POST method in my controller. On average, this method runs between 15 and 20 seconds.
I'm using aync false in that call because I need to wait the answer to know which way to go. But, when i using async false my loading (gif) isn't showed.
$(document).ajaxStart(function() {
$('#overlay').show();
});
$(document).ajaxStop(function() {
$('#overlay').hide();
});
What is the best way to resolve it?
EDIT 1
I have the save function that performs multiple validations and calls the method in the controller:
function salvarInformacoes(pedidos, ums, callback) {
$.ajax({
url: 'PlanoCortes/SalvarInformacoes',
type: 'POST',
data: {
sglDeposito: $("#ddl-Deposito option:selected").text(),
codUnimetPCP: $('#ddl-Um-sip').val(),
numEtapa: $("#ddl-Operacao").val(),
rotinaUM: $('#chk-Rotina-UM').is(":checked"),
dscEtapa: $("#ddl-Operacao option:selected").text(),
dadosPedidosJson: pedidos,
dadosUMsJson: ums,
corteVirtual: corteVirtual
},
success: callback
});
}
function salvar() {
var resultado = false;
...
salvarInformacoes(JSON.stringify(pedidos), JSON.stringify(ums), myCallback);
function myCallback(retorno) {
if (retorno.success != false) {
...
}
else {
resultado = false;
return;
}
resultado = true;
}
return resultado;
}
...
Before the method "myCallback" is called, the function return false. In this way, the code inside the statement below is never executed:
if (salvar()) {
...
}
What is the best way to resolve it?
Don't use async: false.
The browser doesn't show the changes because async: false makes the operation not asynchronous and locks the browser. Keep asynchronous code asynchronous and you can do other things while that code is executing.
I need to wait the answer to know which way to go.
This is the result of a design flaw somewhere in the code. You might try looking through the question and answers here for some help. Essentially you don't want to block the client-side code while waiting for the response, but instead want to handle the response when it arrives.

Wait for an ajax request to complete in React?

Below is my react code I want that firstly the ajax code should execute then the rest of the code should execute.
expected output in console:
inside ajax
outside ajax
current output in console :
outside ajax
inside ajax
import React from 'react';
import request from 'superagent'
const UserItems = () => {
request.get('http://localhost:4000/user/1/items.json')
.then((res, err) => {
if (err) {
console.log("errror found")
}
var data = JSON.parse(res.text)
console.log("inside ajax")
console.log(data)
})
console.log("outside ajax")
console.log(data)
};
export default UserItems;
Any suggestion !!!
As hainguyen points out, ajax is typically asynchronous so the code afterwards will run until the request is complete, at which time the inner function is executed. So the outer console logs will almost certainly run first in your code. While there are ways around this as hainguyen points out, most recommend against it. Ajax is something which takes time, and therefore your code structure should reflect that. If you ever find yourself wanting to run code while the ajax request is in process, you might dislike a synchronous structure. My "I wait for no one" log shows the power of an asynchronous approach since that logic will run quickly while you would normally be waiting on the request without being able to do anything.
Rather than make it synchronous why not use functions to handle the asynchronous behavior better like wrapping whatever you want to run after the inside console log in a function: (I called it outside()) This will output "inside ajax", "outside ajax". This way you can create dependencies on your ajax return and still have the option for running stuff in the meantime.
import React from 'react';
import request from 'superagent';
const UserItems = () => {
request.get('http://localhost:4000/user/1/items.json')
.then((res, err) => {
if (err) {
console.log("errror found");
}
var data = JSON.parse(res.text);
console.log("inside ajax");
console.log(data);
outside();
});
function outside(){
console.log("outside ajax");
console.log(data);
}
console.log("I wait for no one, run me as quick as possible!");
};
export default UserItems;
I don't know about request library but ajax is async by default. If you want ajax perform sync request, you should do something like this:
function getRemote() {
return $.ajax({
type: "GET",
url: remote_url,
async: false
}).responseText;
}
Important line: async: false

How do I prevent Ajax calls from keeping a session alive?

I'm using cookie authentication in MVC5. My web pages rely heavily on authenticated as well as unauthenticated Ajax calls every 1-5 seconds to keep data updated. Consequently, my users never log out of the site.
My ideal scenario: If a user is actively browsing or conducting actions on my site, keep the session alive. If they have left a page open after 10 minutes, I'd like their session to timeout and I’'ll use the failing Ajax calls to redirect to a login page. I think this would best be accomplished at the controller or action level.
I tried controlling the session state behavior as suggested below but the session still did not time out. After 65 seconds of hitting ReadOnly/Public once per second, I call ReadOnly/Authorized and successfully retrieve data from it.
Here is my CookieAuthentication configuration.
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(1),
});
}
My test page:
<div id="public"></div>
<div id="authorized"></div>
#section scripts{
<script>
function poll(times) {
var url = '/ReadOnly/Public';
$.ajax({
url: url,
dataType: 'json',
data: null,
cache: false,
success: function (data) {
$('#public').html(times + ' ' + data.test);
},
error: function (data) {
$('#public').html(times + ' ' + 'failed');
}
});
};
function checkAuth(times) {
var url = '/ReadOnly/Authorized';
$.ajax({
url: url,
dataType: 'json',
data: null,
cache: false,
success: function (data) {
$('#authorized').html(times + ' ' + data.test);
},
error: function (data) {
$('#authorized').html(times + ' ' + 'failed');
}
});
};
$(function () {
var times = 1;
setInterval(function () {
poll(times);
times++;
}, 1000);
setInterval(function () {
checkAuth(times);
}, 65000);
});
</script>
}
and test controller code (tried this with both the disabled and readonly options)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.SessionState;
namespace SessionTest.Controllers
{
[SessionState(SessionStateBehavior.ReadOnly)]
public class ReadOnlyController : Controller
{
[Authorize]
public ActionResult Authorized()
{
return Json(new
{
test = "ReadOnly and Authorized"
}, JsonRequestBehavior.AllowGet);
}
public ActionResult Public()
{
return Json(new
{
test = "ReadOnly and Public"
}, JsonRequestBehavior.AllowGet);
}
}
}
Maybe you need to have 2 separate web apps. One is for serving authenticated requests. Another one is for all public requests.
That's similar to how the Google Analytics script creates and maintains its own Session on Google side about your site without impacting your web application's internal session management. Otherwise, you will get stuck with the default behavior of ASP .NET the way it is handling cookies and keeps session alive.
Good luck.
I wouldn't implement a timeout in this situation. In fact I try to avoid them unless there is a fundamental and key reason why they are necessary, otherwise they just become an annoyance.
However if you do feel you need one, I would implement it in this case, by creating a separate javascript function which has a timer, and that is reset with user input. If the timer completes an ajax call is performed that executes a manual session invalidation on server side.
I would configure the listener method or class to not use session which will prevent it from being extended.
There are attributes available for both methods and controllers that provides different session modes.
More info here:
http://www.dotnet-tricks.com/Tutorial/mvc/906b060113-Controlling-Session-Behavior-in-Asp.Net-MVC4.html
Ajax calls will keep the session alive.
One approach will be to set a timeout on client side to delete cookie after some time.
I'm not sure you have anymore options.
If the calls every 5 sec are only to non-authenticated request, just keep the cookie out of the ajax request.
I think the sliding expiration is set to true by default.
I think perhaps when the call that is made to the action Public, it's made with cookie and thus extending the timeout.
public ActionResult Public()
{
return Json(new
{
test = "ReadOnly and Public"
}, JsonRequestBehavior.AllowGet);
}
If I set this below: (SlidingExpiration = false). I get the failed message.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(1.0),
SlidingExpiration = false
//Provider = new CookieAuthenticationProvider
//{
// OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
// validateInterval: TimeSpan.FromMinutes(30),
// regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
//}
});

Resources