Статьи

Что, черт возьми, означает «ошибка скрипта»?

Эта статья была создана в сотрудничестве с 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 только понимание ошибок, возникающих в том же домене. Все, что мы знаем, это то, что произошла ошибка — больше ничего!

Я не плохой человек, правда!

Несмотря на благие намерения браузеров, есть несколько действительно веских причин, по которым вы хотите получить представление об ошибках, возникающих в сценариях из разных источников:

  1. Файлы JavaScript вашего приложения обслуживаются с другого имени хоста (например, static.sentry.io/app.js).
  2. Вы используете библиотеки, обслуживаемые из CDN сообщества, например cdnjs или Google Hosted Libraries.
  3. Вы работаете с коммерческой сторонней библиотекой 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.)

Это оно! Счастливый мониторинг ошибок .