Внешний интерфейс повторяет несуществующий запрос!

Vue.js

предисловие

Однажды начальник неожиданно позвал меня на свое место и указал на экран. Когда я это увидел, группа эксплуатации и обслуживания была в шоке! Есть авария, есть ошибка! ? Ситуация заключается в том, что системные данные кошелька ненормальны, и коллеги из бэкграунда подтвердили, что одно и то же ручное пакетное пополнение было инициировано дважды.Подозревается, что внешний интерфейс может плохо контролироваться, и запрос на пакетное пополнение инициировался неоднократно. Испугавшись, я быстро проверил соответствующий код и обнаружил, что в интерфейсе «после нажатия кнопки появится всплывающее окно, чтобы напомнить пользователю, следует ли действовать или нет». Это точно не моя сковорода.

Если нет всплывающего окна для второго подтверждения, если вы случайно несколько раз нажмете на интерфейс, вы действительно инициируете несколько запросов и, возможно, пойдете в финансовую комнату, чтобы оплатить счет на следующий день. Это показывает, насколько важно предотвратить повторные запросы!

место происшествия

Вы спросите, как это предотвратить? Не волнуйтесь, сначала посмотрите на те сценарии, которые вызывают повторяющиеся запросы:

  1. Скорость быстро, все равно, дважды нажмите кнопку действия.
  2. Я нажал на кнопку очень осторожно, так как ответ на запрос был медленным, а подсказки на странице не было, я подозревал, что последний клик не подействовал, и снова нажал на кнопку операции.
  3. Я нажимал кнопку очень осторожно, потому что ответ на запрос был медленным, а на странице не было никаких подсказок, обновите страницу и снова нажмите кнопку действия.

Интерфейсное решение

Мы можем назначить правильное лекарство:

  1. Кнопка управления нажимается несколько раз в течение короткого периода времени, и нажатие после первого нажатия является недействительным.
  2. Кнопка управления, повторный щелчок не имеет никакого эффекта, пока запрос, вызванный нажатием кнопки, не ответит.
  3. Настройте специальные URL-адреса, а затем контролируйте минимальный интервал времени между запросами к этим URL-адресам. Если интервал между повторным запросом и предыдущим запросом очень мал, во всплывающем окне второй раз будет предложено продолжить операцию.

Предотвращение непреднамеренных повторных нажатий кнопок

Добавьте элементы управления к кнопкам, вcontrolВ миллисекундах события щелчка после первого события щелчка не выполняются.

<template>
    <button @click="handleClick"></button>
</templage>
<script>
export default {
    methods: {
        handleClick(event) {
            if (this.disabled) return;
            if (this.notAllowed) return;
            // 点击完多少秒不能继续点
            this.notAllowed = true;
            setTimeout(()=>{
                this.notAllowed = false;
            }, this.control)
            this.$emit('click', event, this);
        }
    }
}
</script>

Конечно, временной интервал можно установить, и по умолчанию он составляет 300 миллисекунд. Наши бессознательные повторяющиеся щелчки обычно не превышают 300 миллисекунд.

Нажатие кнопки немедленно отключается, и ответ может быть возвращен, прежде чем продолжить нажатие.

Для конкретного эффекта см. Следующий GIF45addefb-f6e9-44ba-acb9-ed9ac11547cd.gif

Экземпляр кнопок, который вызывает клик, передается в конфигурацию Fetch. Код выглядит следующим образом:

doQuery: function (button) {
	this.FesApi.fetch(`generalcard/query`, {
		sub_card_type: this.query.sub_card_type,
		code_type: this.query.code_type,
		title: this.query.title,
		card_id: this.query.card_id,
		page_info: {
			pageSize: this.paginationOption.page_info.pageSize,
			currentPage: this.paginationOption.page_info.currentPage
		}
	}, {
		//看这里,加上下面一行代码就行。。so easy
		button: button
	}).then(rst => {
		// 成功处理
	});
}

Внутри функции выборки установите кнопкуdisabled=true, а когда придет ответ, установитеdisabled=falseкод показывает, как показано ниже:

const action = function (url, data, option) {
    // 如果传了button
    if (option.button) {
        option.button.currentDisabled = true;
    }
    // 记录日志
    const log = requsetLog.creatLog(url, data);

    return param(url, data, option)
        .then(success, fail)
        .then((response) => {
            requsetLog.changeLogStatus(log, 'success');
            if (option && option.button) {
                option.button.currentDisabled = false;
            }
            return response;
        })
        .catch((error) => {
            requsetLog.changeLogStatus(log, 'fail');
            if (option && option.button) {
                option.button.currentDisabled = false;
            }
            error.message && window.Toast.error(error.message);
            throw error;
        });
};

Начать с рута, убить одним выстрелом

Когда страница обновляется и статус страницы сбрасывается, повторное нажатие кнопки в это время будет считаться первым нажатием, и статус кнопки снова станет доступным для нажатия. Мы можем установить, какие адреса запросов важны, и интервал их запросов не может быть слишком маленьким. Если он слишком мал, на странице всплывает оверлей, чтобы попросить пользователя продолжить выполнение.fd9a6a98-426c-46b6-8dd4-34f99f1b0dba.pngКод установки выглядит следующим образом:

this.FesApi.setImportant({
	'generalcard/action': {
		control: 10000,
		message: '您在十秒内重复发起手工清算操作,是否继续?'
	}
})

Код реализации выглядит следующим образом:

api.fetch = function (url, data, option) {
    if (requsetLog.importantApi[url]) {
        const logs = requsetLog.getLogByURL(url, data);
        if (logs.length > 0) {
            const compareLog = logs[logs.length - 1];
            if (compareLog.status === 'compare') {
                requsetLog.creatLog(url, data, 'notAllowed');
                return {
                    then: () => {}
                };
            }
            const importantApiOption = requsetLog.importantApi[url];
            const control = importantApiOption.control || 10000;
            const message = importantApiOption.message || util.format('fesMessages.importInterfaceTip', { s: control / 1000 });
            if (new Date().getTime() - compareLog.timestamp < control) {
                const oldStatus = compareLog.status;
                requsetLog.changeLogStatus(compareLog, 'compare');
                return new Promise(((resolve, reject) => {
                    window.Message.confirm(util.format('fesMessages.tip'), message).then((index) => {
                        if (compareLog.status === 'compare') {
                            requsetLog.changeLogStatus(compareLog, oldStatus);
                        }
                        if (index === 0) {
                            resolve(action(url, data, option));
                        } else {
                            reject(new Error('不允许相同操作间隔过小'));
                        }
                    });
                }));
            }
            return action(url, data, option);
        }
        return action(url, data, option);
    }
    return action(url, data, option);
};

Злоумышленники могут обойти нормальный процесс и имитировать множественные запросы, поэтому недостаточно предотвратить повторные запросы на странице интерфейса. Фоновый интерфейс должен быть более надежным и идемпотентным.

рекламировать

Fes.js имеет эти три встроенные возможности для предотвращения повторных запросов.Это консольное приложение для управления, которое предоставляет инструменты командной строки для начального проекта, разработки и отладки, компиляции и упаковки, а также встроенный макет, разрешения, словарь данных , управление состоянием, API и т. д. Модуль, файловая структура каталогов маршрутизируется, пользователям нужно только писать содержимое страницы. Общие возможности встроенной консоли управления, основанные на Vue.js, позволяют пользователям писать меньше и проще. После полировки во многих проектах он имеет тенденцию быть стабильным.

Адрес проекта с открытым исходным кодом Fes.js: