Search code examples
ajaxasp.net-web-apixmlhttprequestreduxaxios

Axios POST requests taking too long and being duplicated at WebApi


Today I've received bug reports that some of our application's POST requests are being duplicated.

These requests results in the creation of objects on the remote database such like Tasks, Meetings, etc, so the duplication of them implies in Tasks being created with the same name, same due date, user, etc.

I've tried to replicate this but it seems to be a random behavior, and the only concurrent symptom reported is that the request takes more time than normal to complete.

I'm currently using the stack React + Redux + Axios + Asp.Net WebApi, and I have reached to the following considerations in order to understand and solve the issue.

Any tips on any of these topics are really appreciated.


Root cause identification

React + Redux:

The action creator that dispatches the request is called just once, on a onClick event. Apparently there's no state change or page refresh that could cause a multiple call of this fuction.

handleClick(event) {

    var postData = {

        title: this.state.text,
        dueDate: this.state.date,     
        assignedUser: this.state.user.id
    };

    this.props.contentPost(postData);
}

Axios:

One of my suspicions is that, for some unknown reason, the users' requests fails, or take to long to complete and then axios supposedly sends the request it again. I've checked it's documentation and issues databases, and didn't found nothing like Automatic Retries or request duplication.

export const contentPost = (props) => {
    return function (dispatch) {
        dispatch({ type: CONTENT_POST_ERROR, payload: null })
        dispatch({ type: CONTENT_POST_LOADING, payload: true });
        dispatch({ type: CONTENT_POST_SUCCESS, payload: null })

        const request = axios({
            method: 'post',
            url: `/api/content`,
            headers: auth.getHttpHeader(),
            validateStatus: function (status) {
                return status >= 200 && status < 300; // default
            },
            data: props
        }).then((res) => {
            dispatch({ type: CONTENT_POST_LOADING, payload: false });
            dispatch({ type: CONTENT_POST_SUCCESS, payload: true, data: res })
        }).catch(err => {
            dispatch({ type: CONTENT_POST_ERROR, payload: err.code })
            dispatch({ type: CONTENT_POST_LOADING, payload: false });
            dispatch({ type: CONTENT_POST_SUCCESS, payload: false, data: null })

        });
    }
};

WebApi

The Controller's method don't have any sort of throttling or "uniqueness token" to identify and prevent duplicated requests. Since this is a random behavior, I would not bet that the Routing or any part of my server side application has any responsibility on this.


Solutions so far...

Axios:

  1. Throttle the axios request at my action creator, to prevent it to be called to often at a given time interval.
  2. Send a client-generated "uniqueness token" in the request body

WebApi

  1. Implement request filtering/throttling, in order to prevent duplicated request, based on body contents at a given time interval.
  2. Receive and handle the "uniqueness token" to prevent post duplicity.

Even with the described above, these solutions looks like an overkill, while the root causes also seems a little too subjective.


Solution

  • I've found out that a late loading state feedback for the users was causing this behavior.

    Some people stills double-click stuff on websites. It was naive not to have prevented this by code.

    There are a lot of ways to accomplish a better user experience in this scenario. I chose to completely change the button while user are sending our POST request, and we are also implementing the throttling at our backend.

    {this.props.content_post_loading &&
        <a className="btn btn-primary disabled">Creating...</a>
        ||
        <a className="btn btn-primary" onClick={this.handleCreateClick}>Create</a>
    }