Эта статья была создана в сотрудничестве с Sentry.io . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.
Если вы уже работали с событием JavaScript onerror
, возможно, вы столкнулись со следующим:
Script error.
«Ошибка сценария» — это то, что браузеры отправляют обратному вызову onerror, когда ошибка возникает из файла JavaScript, который подается из другого источника (другого домена, порта или протокола). Это больно, потому что, хотя и происходит ошибка, вы не знаете, что это за ошибка, и из какого кода она происходит. В этом и заключается window.onerror
цель window.onerror
— получить представление о неперехваченных ошибках в вашем приложении.
Причина: сценарии перекрестного происхождения
Чтобы лучше понять, что происходит, рассмотрим следующий пример HTML-документа, гипотетически предоставленного с http://example.com/test:
<!doctype html> <html> <head> <title>example.com/test</title> </head> <body> <script src="http://another-domain.com/app.js"></script> <script> window.onerror = function (message, url, line, column, error) { console.log(message, url, line, column, error); } foo(); // call function declared in app.js </script> </body> </html>
Вот содержимое http://another-domain.com/app.js. Он объявляет одну функцию foo, вызов которой всегда будет вызывать ReferenceError.
// another-domain.com/app.js function foo() { bar(); // ReferenceError: bar is not a function }
Когда этот документ загружается в браузер и выполняется JavaScript, на консоль выводится следующее (регистрируется с помощью обратного вызова window.onerror
):
"Script error.", "", 0, 0, undefined
Это не ошибка JavaScript — браузеры намеренно скрывают ошибки, возникающие из файлов сценариев из разных источников по соображениям безопасности. Это во избежание непреднамеренной утечки скриптом потенциально конфиденциальной информации в onerror
вызов onerror
который он не контролирует. По этой причине браузеры дают window.onerror
только понимание ошибок, возникающих в том же домене. Все, что мы знаем, это то, что произошла ошибка — больше ничего!
Я не плохой человек, правда!
Несмотря на благие намерения браузеров, есть несколько действительно веских причин, по которым вы хотите получить представление об ошибках, возникающих в сценариях из разных источников:
- Файлы JavaScript вашего приложения обслуживаются с другого имени хоста (например, static.sentry.io/app.js).
- Вы используете библиотеки, обслуживаемые из CDN сообщества, например cdnjs или Google Hosted Libraries.
- Вы работаете с коммерческой сторонней библиотекой JavaScript, которая обслуживается только с внешних серверов.
Но не волнуйся! Чтобы получить представление об ошибке JavaScript, обслуживаемой этими файлами, требуется всего несколько простых настроек.
Исправление: атрибуты и заголовки CORS
Чтобы получить представление об исключении JavaScript, создаваемом сценариями, происходящими из разных источников, необходимо выполнить две вещи.
1. Добавьте атрибут сценария crossorigin="anonymous"
<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
Это говорит браузеру, что целевой файл должен быть выбран «анонимно». Это означает, что никакая потенциально идентифицирующая пользователя информация, такая как cookie-файлы или учетные данные HTTP, не будет передана браузером на сервер при запросе этого файла.
2. Добавьте HTTP-заголовок Cross Origin **
Access-Control-Allow-Origin: \*
CORS — это сокращение от Cross Origin Resource Sharing, и это набор API (в основном, HTTP-заголовков), которые определяют, как файлы должны загружаться и обслуживаться в разных источниках.
Установив Access-Control-Allow-Origin: \*
, сервер указывает браузерам, что любой источник может извлечь этот файл. Кроме того, вы можете ограничить его только известным источником, который вы контролируете:
$ curl --head https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.js | \ grep -i "access-control-allow-origin" Access-Control-Allow-Origin: *
После того, как оба эти шага будут выполнены, любые ошибки, вызванные этим сценарием, будут сообщаться в window.onerror
, как и любой обычный сценарий того же домена. Таким образом, вместо «Ошибка onerror
пример с самого начала выдаст:
"ReferenceError: bar is not defined", "http://another-domain.com/app.js", 2, 1, [Object Error]
Boom! Вы сделали — «Ошибка сценария» больше не будет преследовать вас и вашу команду.
Альтернативное решение: try/catch
Иногда мы не можем настраивать заголовки HTTP сценариев, которые использует наше веб-приложение. В таких ситуациях есть альтернативный подход: использование try/catch
.
Снова рассмотрим оригинальный пример, на этот раз с помощью try/catch
:
<!-- note: crossorigin="anonymous" intentionally absent --> <script src="http://another-domain.com/app.js"></script> <script> window.onerror = function (message, url, line, column, error) { console.log(message, url, line, column, error); } try { foo(); // call function declared in app.js } catch (e) { console.log(e); throw e; // intentionally re-throw (caught by window.onerror) } </script>
Для потомков some-domain.com/app.js снова выглядит так:
// another-domain.com/app.js function foo() { bar(); // ReferenceError: bar is not a function }
Выполнение примера HTML выведет следующие две записи на консоль:
=> ReferenceError: bar is not defined at foo (http://another-domain.com/b.js:2:3) at http://example.com/test/:15:3 => "Script error.", "", 0, 0, undefined
Первый оператор консоли — из try/catch
— сумел получить объект ошибки с типом, сообщением и трассировкой стека, включая имена файлов и номера строк. Второй оператор консоли из window.onerror
, опять же, может выводить только «Ошибка сценария».
Теперь, значит ли это, что вам нужно try/catch
весь ваш код? Возможно нет. Если вы можете легко изменить свой HTML-код и указать заголовки CORS в своих CDN, предпочтительно сделать это и придерживаться window.onerror
.
Но если вы не контролируете эти ресурсы, использование try/catch
для переноса стороннего кода — верный (хотя и утомительный) способ получить представление об ошибках, возникающих в сценариях с несколькими источниками.
Примечание: по умолчанию raven.js, JavaScript Sentry SDK , тщательно использует встроенные методы, чтобы попытаться автоматически обернуть ваш код в блоки try/catch
. Это делается для того, чтобы попытаться перехватить сообщения об ошибках и составить следы от всех ваших сценариев, независимо от того, откуда они поступили. По-прежнему рекомендуется устанавливать атрибуты и заголовки CORS, если это возможно.
Конечно, существует множество коммерческих инструментов и инструментов с открытым исходным кодом, которые выполняют всю тяжелую работу по составлению отчетов на стороне клиента. (Psst: вы можете попробовать Sentry для отладки JavaScript.)
Это оно! Счастливый мониторинг ошибок .