7

I develop a web application. Client side - Javascript/Typescript + AngularJS Server side - c# - asp.net web api 2 IIS Express as host.

I noticed a wierd issue occuring in which POST requets reach the server more than once. For example, one scenario in which requests are sent multiple times, each request for a specific folder email is sent 3 times. I also see those request sent three times in the Network tab.

public class mailsService extends ImailsService{

    static instance : ImailsService;

    // Dictionary of folder -> emails
    public Folders;


    public mailsService(){
        var scope = this;
        myInterval = setInterval(function(){
                    // Making this request until getting a successful response - waiting for server to go up
            scope.webService.get("url to get my own place").success(data => {
                scope.myLocation = data;
                scope.isLocationInit = true;
                getAllMails();
                window.cleanInterval(myInterval);
            })
        }, 3000);
    }

    // This function happen on it's own when running the client code.
    // The service is a singleton therefore this code runs only once.
    public getAllMails() {
    if (!this.isLocationInit)
        return;
    var scope = this;
    scope.webService.get("url to get all folders").then(reponse => {
        var folders = response.data;

        for (var i = 1; i <= folders.length; i ++){
                return scope.get("url to get the mails inside specific folder").then(initMails.bind(null, i));
        }});
    }

    public initMails (folderId : number, response){
        mailsService.instance.Folders[folderId].push(response.data);
    }
}

public class webService extends IwebService {
    public get(url : string){
        return this.$http.get(url);
    }

    public post(url : string, data : any){
        return this.$http.post(url, data);
    }
}


// Server side:

// Cross-Origin requests
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class MailsController : ApiController 
{
    private IMailsService _mailsService;

    public MailsController(IMailsService service)
    {
        this._mailsService = service;
    }

    public IHttpActionResult GetAllFolders()
    {
        var result = _mailsService.GetAllFolders();
        if (result == null)
            return BadRequest("...");

        return Ok(result);
    }

    public IHttpActionResult GetFolderEmails(int folderId)
    {
        var result = _mailsService.GetFolderEmails(folderId);
        if (result == null)
            return BadRequest("...");

        return Ok(result);
    }
}

The controller fitting method hits twice or even more, although it is called only once from the cliend side code.

Is this something configurably? IIS wierd thing? I seriously don't know where to start looking.

  • 3
    It's unlikely that it's IIS or server side. I've seen something like this before and it turns out that the javascript event that ended up calling the $http.post was attached to the click event twice. So when the user clicked, the javascript event fired twice, which caused two POSTs to the server even though the code only looked like it should happen once. To check, view the network tab of your browser dev tools and see how many network requests there are. If two, focus on the javascript - use the browser dev tools to breakpoint the $http.post line and confirm how often that line is hit. – Chris Simon Aug 23 '16 at 01:31
  • Kindly check whether your browser is doing a preflight request or not. :) To know more about preflight please have a look [here](http://stackoverflow.com/questions/8685678/cors-how-do-preflight-an-httprequest) – Swagata Prateek Aug 23 '16 at 19:30
  • I checked the network tab and the requests are sent multiple times. The client side code is definitely running once, don't know what could be the reason for this to happen. –  Aug 25 '16 at 08:49
  • Something that has probably unrelated , but i would have write `var myInterval = setInterval(function(){` to force the closure scope. – aprovent Aug 31 '16 at 11:10
  • 1
    I assume that `cleanInterval` in the code sample is a typo and that you have `clearInterval` in your actual code. – ConnorsFan Sep 01 '16 at 20:44
  • Is "reponse => response" (missing "s" in "reponse") a typo or something that TypeScript takes care of? Is this a 1-to-1 copy of your production code? What does your compiled JavaScript look like? – Uli Sep 02 '16 at 22:08

4 Answers4

1

In mailsService, you set an interval timer to call the Web service at every 3 seconds. Is it possible that, by the time the response has come back and the whole processing has been done, the timer has ticked once or twice and triggered the procedure again? You could test that by changing the interval to shorter and longer values and see if that affects the number of calls to the server.

One way of reducing the risk would be to call window.clearInterval at the very beginning of your success call back function, before the call to getAllMails:

scope.webService.get("url to get my own place").success(data => {
    window.clearInterval(myInterval);
    scope.myLocation = data;
    scope.isLocationInit = true;
    getAllMails();
}

Another solution would be to avoid using setInterval. You could execute scope.webService.get and wait for the response. In case of error, you would call the service again after a delay, using setTimeout:

public mailsService() {
    var scope = this;
    (function callWebService () {
        scope.webService.get("url to get my own place")
            .success(data => {
                scope.myLocation = data;
                scope.isLocationInit = true;
                getAllMails();
            })
            .error(data => { 
                setTimeout(callWebService, 3000); 
            });
    })();
}

Note: I am not familiar with Typescript, so there may be a better way to implement it.

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
0

I don't have comment facility either, but what I always do is load a fresh new page right after posting forms, cause if a user hits the browser's Back button (they often do that), your form will be sent again. If you instead load a new page, your form will not load again. Not sure if your problem always happens, even with testing, in that case, I guess you need to post more code.

Saskia
  • 474
  • 5
  • 13
  • Hi, I added the entire flow of a secenario that causes this problem. –  Aug 25 '16 at 08:48
0

Is this happening in all browsers? I have recently seen a script tag with an empty src attribute cause Firefox to seemingly load the page multiple times.

Learning2Code
  • 521
  • 9
  • 21
0

I don't see a candidate for POST in your web API code you posted nor a POST call in your JavaScript as well. Am I missing something?

As you have mentioned For example, one scenario in which requests are sent multiple times, each request for a specific folder email is sent 3 times, I'm assuming its the getAllMails() method thats getting called multiple times. The reason for this is the setInterval logic in the below method:

public mailsService(){
        var scope = this;
        myInterval = setInterval(function(){
                    // Making this request until getting a successful response - waiting for server to go up
            scope.webService.get("url to get my own place").success(data => {
                scope.myLocation = data;
                scope.isLocationInit = true;
                getAllMails();
                window.cleanInterval(myInterval);
            })
        }, 3000);

    }

There is a potential chance that this call would be made multiple times - consider a scenario where the very first call (0th second) made to scope.webService.get("url to get my own place") took 6 seconds to complete - this will end up having an ajax call in the 3rd and 6th second due to the setInterval logic as the setInterval is cleared only after the success callback of the request.

If the logic behind having setInterval is as mentioned in the comment // Making this request until getting a successful response - waiting for server to go up, then I would suggest you to go for a different approach.

I'm not sure what would be your server rsponding if it is "not up", but assuming it returns some error code in that scenario (say, error code -1), then do a check in your error call back and if the error code is something related to "server is not up", then call the same method again. Something like:

public mailsService(){
        var scope = this;

        scope.webService.get("url to get my own place")
          .then(function(data)   {
                scope.myLocation = data;
                scope.isLocationInit = true;
                getAllMails();      
            },
            function(ex)
            {
              if(ex.status === "-1")
               {   
                 //service seems to be 'not up', keep hitting the service
                 mailsService();      
               }
            });
       }
Al Fahad
  • 2,378
  • 5
  • 28
  • 37
Developer
  • 6,240
  • 3
  • 18
  • 24