Как обрабатывать ошибки на сайте

Ошибки — это хорошо. Автор материала, перевод которого мы сегодня публикуем, говорит, что уверен в том, что эта идея известна всем. На первый взгляд ошибки кажутся чем-то страшным. Им могут сопутствовать какие-то потери. Ошибка, сделанная на публике, вредит авторитету того, кто её совершил. Но, совершая ошибки, мы на них учимся, а значит, попадая в следующий раз в ситуацию, в которой раньше вели себя неправильно, делаем всё как нужно.

Выше мы говорили об ошибках, которые люди совершают в обычной жизни. Ошибки в программировании — это нечто иное. Сообщения об ошибках помогают нам улучшать код, они позволяют сообщать пользователям наших проектов о том, что что-то пошло не так, и, возможно, рассказывают пользователям о том, как нужно вести себя для того, чтобы ошибок больше не возникало.

Этот материал, посвящённый обработке ошибок в JavaScript, разбит на три части. Сначала мы сделаем общий обзор системы обработки ошибок в JavaScript и поговорим об объектах ошибок. После этого мы поищем ответ на вопрос о том, что делать с ошибками, возникающими в серверном коде (в частности, при использовании связки Node.js + Express.js). Далее — обсудим обработку ошибок в React.js. Фреймворки, которые будут здесь рассматриваться, выбраны по причине их огромной популярности. Однако рассматриваемые здесь принципы работы с ошибками универсальны, поэтому вы, даже если не пользуетесь Express и React, без труда сможете применить то, что узнали, к тем инструментам, с которыми работаете.

Код демонстрационного проекта, используемого в данном материале, можно найти в этом репозитории.

1. Ошибки в JavaScript и универсальные способы работы с ними

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

throw new Error('something went wrong')

В ходе выполнения этой команды будет создан экземпляр объекта Error и будет сгенерировано (или, как говорят, «выброшено») исключение с этим объектом. Инструкция throw может генерировать исключения, содержащие произвольные выражения. При этом выполнение скрипта остановится в том случае, если не были предприняты меры по обработке ошибки.

Начинающие JS-программисты обычно не используют инструкцию throw. Они, как правило, сталкиваются с исключениями, выдаваемыми либо средой выполнения языка, либо сторонними библиотеками. Когда это происходит — в консоль попадает нечто вроде ReferenceError: fs is not defined и выполнение программы останавливается.

▍Объект Error

У экземпляров объекта Error есть несколько свойств, которыми мы можем пользоваться. Первое интересующее нас свойство — message. Именно сюда попадает та строка, которую можно передать конструктору ошибки в качестве аргумента. Например, ниже показано создание экземпляра объекта Error и вывод в консоль переданной конструктором строки через обращение к его свойству message.

const myError = new Error('please improve your code')
console.log(myError.message) // please improve your code

Второе свойство объекта, очень важное, представляет собой трассировку стека ошибки. Это — свойство stack. Обратившись к нему можно просмотреть стек вызовов (историю ошибки), который показывает последовательность операций, приведшую к неправильной работе программы. В частности, это позволяет понять — в каком именно файле содержится сбойный код, и увидеть, какая последовательность вызовов функций привела к ошибке. Вот пример того, что можно увидеть, обратившись к свойству stack.

Error: please improve your code
 at Object.<anonymous> (/Users/gisderdube/Documents/_projects/hacking.nosync/error-handling/src/general.js:1:79)
 at Module._compile (internal/modules/cjs/loader.js:689:30)
 at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
 at Module.load (internal/modules/cjs/loader.js:599:32)
 at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
 at Function.Module._load (internal/modules/cjs/loader.js:530:3)
 at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
 at startup (internal/bootstrap/node.js:266:19)
 at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

Здесь, в верхней части, находится сообщение об ошибке, затем следует указание на тот участок кода, выполнение которого вызвало ошибку, потом описывается то место, откуда был вызван этот сбойный участок. Это продолжается до самого «дальнего» по отношению к ошибке фрагмента кода.

▍Генерирование и обработка ошибок

Создание экземпляра объекта Error, то есть, выполнение команды вида new Error(), ни к каким особым последствиям не приводит. Интересные вещи начинают происходить после применения оператора throw, который генерирует ошибку. Как уже было сказано, если такую ошибку не обработать, выполнение скрипта остановится. При этом нет никакой разницы — был ли оператор throw использован самим программистом, произошла ли ошибка в некоей библиотеке или в среде выполнения языка (в браузере или в Node.js). Поговорим о различных сценариях обработки ошибок.

▍Конструкция try…catch

Блок try...catch представляет собой самый простой способ обработки ошибок, о котором часто забывают. В наши дни, правда, он используется гораздо интенсивнее чем раньше, благодаря тому, что его можно применять для обработки ошибок в конструкциях async/await.

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

const a = 5

try {
    console.log(b) // переменная b не объявлена - возникает ошибка
} catch (err) {
    console.error(err) // в консоль попадает сообщение об ошибке и стек ошибки
}

console.log(a) // выполнение скрипта не останавливается, данная команда выполняется

Если бы в этом примере мы не заключили бы сбойную команду console.log(b) в блок try...catch, то выполнение скрипта было бы остановлено.

▍Блок finally

Иногда случается так, что некий код нужно выполнить независимо от того, произошла ошибка или нет. Для этого можно, в конструкции try...catch, использовать третий, необязательный, блок — finally. Часто его использование эквивалентно некоему коду, который идёт сразу после try...catch, но в некоторых ситуациях он может пригодиться. Вот пример его использования.

const a = 5

try {
    console.log(b) // переменная b не объявлена - возникает ошибка
} catch (err) {
    console.error(err) // в консоль попадает сообщение об ошибке и стек ошибки
} finally {
    console.log(a) // этот код будет выполнен в любом случае
}

▍Асинхронные механизмы — коллбэки

Программируя на JavaScript всегда стоит обращать внимание на участки кода, выполняющиеся асинхронно. Если у вас имеется асинхронная функция и в ней возникает ошибка, скрипт продолжит выполняться. Когда асинхронные механизмы в JS реализуются с использованием коллбэков (кстати, делать так не рекомендуется), соответствующий коллбэк (функция обратного вызова) обычно получает два параметра. Это нечто вроде параметра err, который может содержать ошибку, и result — с результатами выполнения асинхронной операции. Выглядит это примерно так:

myAsyncFunc(someInput, (err, result) => {
    if(err) return console.error(err) // порядок работы с объектом ошибки мы рассмотрим позже
    console.log(result)
})

Если в коллбэк попадает ошибка, она видна там в виде параметра err. В противном случае в этот параметр попадёт значение undefined или null. Если оказалось, что в err что-то есть, важно отреагировать на это, либо так как в нашем примере, воспользовавшись командой return, либо воспользовавшись конструкцией if...else и поместив в блок else команды для работы с результатом выполнения асинхронной операции. Речь идёт о том, чтобы, в том случае, если произошла ошибка, исключить возможность работы с результатом, параметром result, который в таком случае может иметь значение undefined. Работа с таким значением, если предполагается, например, что оно содержит объект, сама может вызвать ошибку. Скажем, это произойдёт при попытке использовать конструкцию result.data или подобную ей.

▍Асинхронные механизмы — промисы

Для выполнения асинхронных операций в JavaScript лучше использовать не коллбэки а промисы. Тут, в дополнение к улучшенной читабельности кода, имеются и более совершенные механизмы обработки ошибок. А именно, возиться с объектом ошибки, который может попасть в функцию обратного вызова, при использовании промисов не нужно. Здесь для этой цели предусмотрен специальный блок catch. Он перехватывает все ошибки, произошедшие в промисах, которые находятся до него, или все ошибки, которые произошли в коде после предыдущего блока catch. Обратите внимание на то, что если в промисе произошла ошибка, для обработки которой нет блока catch, это не остановит выполнение скрипта, но сообщение об ошибке будет не особенно удобочитаемым.

(node:7741) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: something went wrong
(node:7741) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. */

В результате можно порекомендовать всегда, при работе с промисами, использовать блок catch. Взглянем на пример.

Promise.resolve(1)
    .then(res => {
        console.log(res) // 1

        throw new Error('something went wrong')

        return Promise.resolve(2)
    })
    .then(res => {
        console.log(res) // этот блок выполнен не будет
    })
    .catch(err => {
        console.error(err) // о том, что делать с этой ошибкой, поговорим позже
        return Promise.resolve(3)
    })
    .then(res => {
        console.log(res) // 3
    })
    .catch(err => {
        // этот блок тут на тот случай, если в предыдущем блоке возникнет какая-нибудь ошибка
        console.error(err)
    })

▍Асинхронные механизмы и try…catch

После того, как в JavaScript появилась конструкция async/await, мы вернулись к классическому способу обработки ошибок — к try...catch...finally. Обрабатывать ошибки при таком подходе оказывается очень легко и удобно. Рассмотрим пример.

;(async function() {
    try {
        await someFuncThatThrowsAnError()
    } catch (err) {
        console.error(err) // об этом поговорим позже
    }

    console.log('Easy!') // будет выполнено
})()

При таком подходе ошибки в асинхронном коде обрабатываются так же, как в синхронном. В результате теперь, при необходимости, в одном блоке catch можно обрабатывать более широкий диапазон ошибок.

2. Генерирование и обработка ошибок в серверном коде

Теперь, когда у нас есть инструменты для работы с ошибками, посмотрим на то, что мы можем с ними делать в реальных ситуациях. Генерирование и правильная обработка ошибок — это важнейший аспект серверного программирования. Существуют разные подходы к работе с ошибками. Здесь будет продемонстрирован подход с использованием собственного конструктора для экземпляров объекта Error и кодов ошибок, которые удобно передавать во фронтенд или любым механизмам, использующим серверные API. Как структурирован бэкенд конкретного проекта — особого значения не имеет, так как при любом подходе можно использовать одни и те же идеи, касающиеся работы с ошибками.

В качестве серверного фреймворка, отвечающего за маршрутизацию, мы будем использовать Express.js. Подумаем о том, какая структура нам нужна для организации эффективной системы обработки ошибок. Итак, вот что нам нужно:

  1. Универсальная обработка ошибок — некий базовый механизм, подходящий для обработки любых ошибок, в ходе работы которого просто выдаётся сообщение наподобие Something went wrong, please try again or contact us, предлагающее пользователю попробовать выполнить операцию, давшую сбой, ещё раз или связаться с владельцем сервера. Эта система не отличается особой интеллектуальностью, но она, по крайней мере, способна сообщить пользователю о том, что что-то пошло не так. Подобное сообщение гораздо лучше, чем «бесконечная загрузка» или нечто подобное.
  2. Обработка конкретных ошибок — механизм, позволяющий сообщить пользователю подробные сведения о причинах неправильного поведения системы и дать ему конкретные советы по борьбе с неполадкой. Например, это может касаться отсутствия неких важных данных в запросе, который пользователь отправляет на сервер, или в том, что в базе данных уже существует некая запись, которую он пытается добавить ещё раз, и так далее.

▍Разработка собственного конструктора объектов ошибок

Здесь мы воспользуемся стандартным классом Error и расширим его. Пользоваться механизмами наследования в JavaScript — дело рискованное, но в данном случае эти механизмы оказываются весьма полезными. Зачем нам наследование? Дело в том, что нам, для того, чтобы код удобно было бы отлаживать, нужны сведения о трассировке стека ошибки. Расширяя стандартный класс Error, мы, без дополнительных усилий, получаем возможности по трассировке стека. Мы добавляем в наш собственный объект ошибки два свойства. Первое — это свойство code, доступ к которому можно будет получить с помощью конструкции вида err.code. Второе — свойство status. В него будет записываться код состояния HTTP, который планируется передавать клиентской части приложения.

Вот как выглядит класс CustomError, код которого оформлен в виде модуля.

class CustomError extends Error {
    constructor(code = 'GENERIC', status = 500, ...params) {
        super(...params)

        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, CustomError)
        }

        this.code = code
        this.status = status
    }
}

module.exports = CustomError

▍Маршрутизация

Теперь, когда наш объект ошибки готов к использованию, нужно настроить структуру маршрутов. Как было сказано выше, нам требуется реализовать унифицированный подход к обработке ошибок, позволяющий одинаково обрабатывать ошибки для всех маршрутов. По умолчанию фреймворк Express.js не вполне поддерживает такую схему работы. Дело в том, что все его маршруты инкапсулированы.

Для того чтобы справиться с этой проблемой, мы можем реализовать собственный обработчик маршрутов и определять логику маршрутов в виде обычных функций. Благодаря такому подходу, если функция маршрута (или любая другая функция) выбрасывает ошибку, она попадёт в обработчик маршрутов, который затем может передать её клиентской части приложения. При возникновении ошибки на сервере мы планируем передавать её во фронтенд в следующем формате, полагая, что для этого будет применяться JSON-API:

{
    error: 'SOME_ERROR_CODE',
    description: 'Something bad happened. Please try again or contact support.'
}

Если на данном этапе происходящие кажется вам непонятным — не беспокойтесь — просто продолжайте читать, пробуйте работать с тем, о чём идёт речь, и постепенно вы во всём разберётесь. На самом деле, если говорить о компьютерном обучении, здесь применяется подход «сверху-вниз», когда сначала обсуждаются общие идеи, а потом осуществляется переход к частностям.

Вот как выглядит код обработчика маршрутов.

const express = require('express')
const router = express.Router()
const CustomError = require('../CustomError')

router.use(async (req, res) => {
    try {
        const route = require(`.${req.path}`)[req.method]

        try {
            const result = route(req) // Передаём запрос функции route
            res.send(result) // Передаём клиенту то, что получено от функции route
        } catch (err) {
            /*
            Сюда мы попадаем в том случае, если в функции route произойдёт ошибка
            */
            if (err instanceof CustomError) {
                /* 
                Если ошибка уже обработана - трансформируем её в 
                возвращаемый объект
                */

                return res.status(err.status).send({
                    error: err.code,
                    description: err.message,
                })
            } else {
                console.error(err) // Для отладочных целей

                // Общая ошибка - вернём универсальный объект ошибки
                return res.status(500).send({
                    error: 'GENERIC',
                    description: 'Something went wrong. Please try again or contact support.',
                })
            }
        }
    } catch (err) {
        /* 
         Сюда мы попадём, если запрос окажется неудачным, то есть,
         либо не будет найдено файла, соответствующего пути, переданному
         в запросе, либо не будет экспортированной функции с заданным
         методом запроса
        */
        res.status(404).send({
            error: 'NOT_FOUND',
            description: 'The resource you tried to access does not exist.',
        })
    }
})

module.exports = router

Полагаем, комментарии в коде достаточно хорошо его поясняют. Надеемся, читать их удобнее, чем объяснения подобного кода, данные после него.

Теперь взглянем на файл маршрутов.

const CustomError = require('../CustomError')

const GET = req => {
    // пример успешного выполнения запроса
    return { name: 'Rio de Janeiro' }
}

const POST = req => {
    // пример ошибки общего характера
    throw new Error('Some unexpected error, may also be thrown by a library or the runtime.')
}

const DELETE = req => {
    // пример ошибки, обрабатываемой особым образом
    throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.')
}

const PATCH = req => {
    // пример перехвата ошибок и использования CustomError
    try {
        // тут случилось что-то нехорошее
        throw new Error('Some internal error')
    } catch (err) {
        console.error(err) // принимаем решение о том, что нам тут делать

        throw new CustomError(
            'CITY_NOT_EDITABLE',
            400,
            'The city you are trying to edit is not editable.'
        )
    }
}

module.exports = {
    GET,
    POST,
    DELETE,
    PATCH,
}

В этих примерах с самими запросами ничего не делается. Тут просто рассматриваются разные сценарии возникновения ошибок. Итак, например, запрос GET /city попадёт в функцию const GET = req =>..., запрос POST /city попадёт в функцию const POST = req =>... и так далее. Эта схема работает и при использовании параметров запросов. Например — для запроса вида GET /city?startsWith=R. В целом, здесь продемонстрировано, что при обработке ошибок, во фронтенд может попасть либо общая ошибка, содержащая лишь предложение попробовать снова или связаться с владельцем сервера, либо ошибка, сформированная с использованием конструктора CustomError, которая содержит подробные сведения о проблеме.
Данные общей ошибки придут в клиентскую часть приложения в таком виде:

{
    error: 'GENERIC',
    description: 'Something went wrong. Please try again or contact support.'
}

Конструктор CustomError используется так:

throw new CustomError('MY_CODE', 400, 'Error description')

Это даёт следующий JSON-код, передаваемый во фронтенд:

{
    error: 'MY_CODE',
    description: 'Error description'
}

Теперь, когда мы основательно потрудились над серверной частью приложения, в клиентскую часть больше не попадают бесполезные логи ошибок. Вместо этого клиент получает полезные сведения о том, что пошло не так.

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

3. Работа с ошибками на клиенте

Теперь пришла пора описать третью часть нашей системы обработки ошибок, касающуюся фронтенда. Тут нужно будет, во-первых, обрабатывать ошибки, возникающие в клиентской части приложения, а во-вторых, понадобится оповещать пользователя об ошибках, возникающих на сервере. Разберёмся сначала с показом сведений о серверных ошибках. Как уже было сказано, в этом примере будет использована библиотека React.

▍Сохранение сведений об ошибках в состоянии приложения

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

Следующее, с чем надо разобраться, заключается в том, что ошибки одного типа нужно показывать в одном стиле. По аналогии с сервером, здесь можно выделить 3 типа ошибок.

  1. Глобальные ошибки — в эту категорию попадают сообщения об ошибках общего характера, приходящие с сервера, или ошибки, которые, например, возникают в том случае, если пользователь не вошёл в систему и в других подобных ситуациях.
  2. Специфические ошибки, выдаваемые серверной частью приложения — сюда относятся ошибки, сведения о которых приходят с сервера. Например, подобная ошибка возникает, если пользователь попытался войти в систему и отправил на сервер имя и пароль, а сервер сообщил ему о том, что пароль неправильный. Подобные вещи в клиентской части приложения не проверяются, поэтому сообщения о таких ошибках должны приходить с сервера.
  3. Специфические ошибки, выдаваемые клиентской частью приложения. Пример такой ошибки — сообщение о некорректном адресе электронной почты, введённом в соответствующее поле.

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

Здесь будет использоваться встроенная в React система управления состоянием приложения, но, при необходимости, вы можете воспользоваться и специализированными решениями для управления состоянием — такими, как MobX или Redux.

▍Глобальные ошибки

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

Сообщение о глобальной ошибке

Теперь взглянем на код, который хранится в файле Application.js.

import React, { Component } from 'react'

import GlobalError from './GlobalError'

class Application extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
        }

        this._resetError = this._resetError.bind(this)
        this._setError = this._setError.bind(this)
    }

    render() {
        return (
            <div className="container">
                <GlobalError error={this.state.error} resetError={this._resetError} />
                <h1>Handling Errors</h1>
            </div>
        )
    }

    _resetError() {
        this.setState({ error: '' })
    }

    _setError(newError) {
        this.setState({ error: newError })
    }
}

export default Application

Как видно, в состоянии, в Application.js, имеется место для хранения данных ошибки. Кроме того, тут предусмотрены методы для сброса этих данных и для их изменения.

Ошибка и метод для сброса ошибки передаётся компоненту GlobalError, который отвечает за вывод сообщения об ошибке на экран и за сброс ошибки после нажатия на значок x в поле, где выводится сообщение. Вот код компонента GlobalError (файл GlobalError.js).

import React, { Component } from 'react'

class GlobalError extends Component {
    render() {
        if (!this.props.error) return null

        return (
            <div
                style={{
                    position: 'fixed',
                    top: 0,
                    left: '50%',
                    transform: 'translateX(-50%)',
                    padding: 10,
                    backgroundColor: '#ffcccc',
                    boxShadow: '0 3px 25px -10px rgba(0,0,0,0.5)',
                    display: 'flex',
                    alignItems: 'center',
                }}
            >
                {this.props.error}
                 
                <i
                    className="material-icons"
                    style={{ cursor: 'pointer' }}
                    onClick={this.props.resetError}
                >
                    close
                </font></i>
            </div>
        )
    }
}

export default GlobalError

Обратите внимание на строку if (!this.props.error) return null. Она указывает на то, что при отсутствии ошибки компонент ничего не выводит. Это предотвращает постоянный показ красного прямоугольника на странице. Конечно, вы, при желании, можете поменять внешний вид и поведение этого компонента. Например, вместо того, чтобы сбрасывать ошибку по нажатию на x, можно задать тайм-аут в пару секунд, по истечении которого состояние ошибки сбрасывается автоматически.

Теперь, когда всё готово для работы с глобальными ошибками, для задания глобальной ошибки достаточно воспользоваться _setError из Application.js. Например, это можно сделать в том случае, если сервер, после обращения к нему, вернул сообщение об общей ошибке (error: 'GENERIC'). Рассмотрим пример (файл GenericErrorReq.js).

import React, { Component } from 'react'
import axios from 'axios'

class GenericErrorReq extends Component {
    constructor(props) {
        super(props)

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            <div>
                <button onClick={this._callBackend}>Click me to call the backend</button>
            </div>
        )
    }

    _callBackend() {
        axios
            .post('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                }
            })
    }
}

export default GenericErrorReq

На самом деле, на этом наш разговор об обработке ошибок можно было бы и закончить. Даже если в проекте нужно оповещать пользователя о специфических ошибках, никто не мешает просто поменять глобальное состояние, хранящее ошибку и вывести соответствующее сообщение поверх страницы. Однако тут мы не остановимся и поговорим о специфических ошибках. Во-первых, это руководство по обработке ошибок иначе было бы неполным, а во-вторых, с точки зрения UX-специалистов, неправильно будет показывать сообщения обо всех ошибках так, будто все они — глобальные.

▍Обработка специфических ошибок, возникающих при выполнении запросов

Вот пример специфического сообщения об ошибке, выводимого в том случае, если пользователь пытается удалить из базы данных город, которого там нет.

Сообщение о специфической ошибке

Тут используется тот же принцип, который мы применяли при работе с глобальными ошибками. Только сведения о таких ошибках хранятся в локальном состоянии соответствующих компонентов. Работа с ними очень похожа на работу с глобальными ошибками. Вот код файла SpecificErrorReq.js.

import React, { Component } from 'react'
import axios from 'axios'

import InlineError from './InlineError'

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
        }

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            <div>
                <button onClick={this._callBackend}>Delete your city</button>
                <InlineError error={this.state.error} />
            </div>
        )
    }

    _callBackend() {
        this.setState({
            error: '',
        })

        axios
            .delete('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                } else {
                    this.setState({
                        error: err.response.data.description,
                    })
                }
            })
    }
}

export default SpecificErrorRequest

Тут стоит отметить, что для сброса специфических ошибок недостаточно, например, просто нажать на некую кнопку x. То, что пользователь прочёл сообщение об ошибке и закрыл его, не помогает такую ошибку исправить. Исправить её можно, правильно сформировав запрос к серверу, например — введя в ситуации, показанной на предыдущем рисунке, имя города, который есть в базе. В результате очищать сообщение об ошибке имеет смысл, например, после выполнения нового запроса. Сбросить ошибку можно и в том случае, если пользователь внёс изменения в то, что будет использоваться при формировании нового запроса, то есть — при изменении содержимого поля ввода.

▍Ошибки, возникающие в клиентской части приложения

Как уже было сказано, для хранения данных о таких ошибках можно использовать состояние тех же компонентов, которое используется для хранения данных по специфическим ошибкам, поступающим с сервера. Предположим, мы позволяем пользователю отправить на сервер запрос на удаление города из базы только в том случае, если в соответствующем поле ввода есть какой-то текст. Отсутствие или наличие текста в поле можно проверить средствами клиентской части приложения.

В поле ничего нет, мы сообщаем об этом пользователю

Вот код файла SpecificErrorFrontend.js, реализующий вышеописанный функционал.

import React, { Component } from 'react'
import axios from 'axios'

import InlineError from './InlineError'

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
            city: '',
        }

        this._callBackend = this._callBackend.bind(this)
        this._changeCity = this._changeCity.bind(this)
    }

    render() {
        return (
            <div>
                <input
                    type="text"
                    value={this.state.city}
                    style={{ marginRight: 15 }}
                    onChange={this._changeCity}
                />
                <button onClick={this._callBackend}>Delete your city</button>
                <InlineError error={this.state.error} />
            </div>
        )
    }

    _changeCity(e) {
        this.setState({
            error: '',
            city: e.target.value,
        })
    }

    _validate() {
        if (!this.state.city.length) throw new Error('Please provide a city name.')
    }

    _callBackend() {
        this.setState({
            error: '',
        })

        try {
            this._validate()
        } catch (err) {
            return this.setState({ error: err.message })
        }

        axios
            .delete('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                } else {
                    this.setState({
                        error: err.response.data.description,
                    })
                }
            })
    }
}

export default SpecificErrorRequest

▍Интернационализация сообщений об ошибках с использованием кодов ошибок

Возможно, сейчас вы задаётесь вопросом о том, зачем нам нужны коды ошибок (наподобие GENERIC), если мы показываем пользователю только сообщения об ошибках, полученных с сервера. Дело в том, что, по мере роста и развития приложения, оно, вполне возможно, выйдет на мировой рынок, а это означает, что настанет время, когда создателям приложения нужно будет задуматься о поддержке им нескольких языков. Коды ошибок позволяют отличать их друг от друга и выводить сообщения о них на языке пользователя сайта.

Итоги

Надеемся, теперь у вас сформировалось понимание того, как можно работать с ошибками в веб-приложениях. Нечто вроде console.error(err) следует использовать только в отладочных целях, в продакшн подобные вещи, забытые программистом, проникать не должны. Упрощает решение задачи логирования использование какой-нибудь подходящей библиотеки наподобие loglevel.

Уважаемые читатели! Как вы обрабатываете ошибки в своих проектах?

При работе над веб-приложениями программисту легко попасть в ловушку: разрабатывать и тестировать только понятные сценарии, в которых всё происходит правильно. К сожалению, в реальности встречаются ситуации, в которых всё идёт не так, как планировалось. Обработка ошибок — важная часть пользовательского опыта любого приложения. Если приложение реагирует на ошибки правильно, ваши пользователи будут знать, что делать дальше, даже если что-то идёт не так.

  • Обработка ошибок в бэкенд- и фронтенд-приложениях: в чём разница
  • Как правильно обрабатывать ошибки
  • Как работать с ошибками в AJAX-запросах и что нужно знать о кодах ответа HTTP
  • Как перехватывать ошибки во фронтенд-приложениях
  • Главное об обработке ошибок во фронтенд-приложениях

Большинство ошибок, с которыми сталкиваются пользователи веб-приложений, можно отнести к одной из перечисленных ниже категорий.

Ошибки ввода

Они возникают, когда приложение по каким-либо причинам не может принять введённую пользователем информацию. Например, такое происходит, если пользовательские данные не проходят валидацию, человек повторно отправляет форму, вводит неуникальный юзернейм, приложение не может найти запрошенные ресурсы и так далее.

Ошибки авторизации

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

Ошибки доступности

Эти ошибки возникают, когда ресурс, нужный для завершения действия пользователя, по каким-то причинам недоступен. Такие ошибки бывают запланированными (плановое обновление сайта) и незапланированными (выход из строя сервера).

Неожиданные ошибки

Это ошибки, которые обычно говорят о багах в приложении, например, о необработанных исключениях.

Практически во всех приложениях случаются ошибки из перечисленных выше категорий. Правильная обработка ошибок — ключ к тому, чтобы пользователи оставались довольными, когда во время работы с приложением случается ошибка.

Примечание — тема обработки ошибок во фронтенд-приложениях подробно рассматривается в рамках профессии «Фронтенд-программист». Базовые курсы в этой профессии, включая «Введение в программирование», «Основы командной строки», «Настройка окружения», «Системы контроля версий», доступны бесплатно после регистрации.

Обработка ошибок в бэкенд- и фронтенд-приложениях: в чём разница

Обработка ожидаемых ошибок в бэкенде веб-приложений обычно происходит так: приложение отвечает сообщением об ошибке или отображает это сообщение пользователю. Неожиданные ошибки ломают нормальный процесс ответа и приводят к отображению общей страницы ошибки.

Плохо настроенные приложения могут даже показывать конечному пользователю информацию о внутренних ошибках. В большинстве случаев бэкенд-приложения не очень хорошо помогают человеку справиться с ошибкой и вернуться к нормальному использованию приложения. Но они хорошо справляются с задачей информирования пользователя об ошибках.

У фронтенд-приложений нет встроенного механизма, позволяющего остановить работу и показать сообщение об ошибке. После возникновения ошибки в JavaScript обычно происходит одно из описанных ниже событий:

  • Приложение работает, но не выполняет действий, которые ожидает пользователь. Самая распространённая реакция пользователей в такой ситуации — попробовать ещё раз в надежде, что в этот раз приложение поведёт себя ожидаемо.
  • Приложение останавливается, но не сообщает об остановке пользователю. Здесь пользователь повторит действие или попробует выполнить новое действие, но у него ничего не получится.
  • Если ошибка происходит достаточно рано, пользователь может увидеть белый экран из-за неудачной попытки приложения отобразить страницу.

Все эти сценарии ужасные с точки зрения пользовательского опыта. Они могут разочаровать пользователя, заставить его чувствовать беспомощность и даже злость. Фронтенд-приложения во многом более гибкие в плане обработки ошибок по сравнению с бэкенд-приложениями. Но позаботиться об обработке ошибок должны разработчики, так как встроенные в браузеры инструменты практически бесполезны для конечных пользователей.

Читайте полезную статью
Что такое магические числа в программировании и как снять это заклятие.

Как правильно обрабатывать ошибки

Есть много способов обработки ошибок в JavaScript-приложениях. Вы можете определить глобальный обработчик ошибок, который будет отображать переданные в него сообщения. Также вы можете построить приложение так, чтобы каждый его компонент самостоятельно обрабатывал ошибки, которые в нём возникают.

Один из простых способов обработки ошибок заключается в том, чтобы создать общую схему для реакции на все ошибки и использовать систему событий браузеров, чтобы перехватывать всплывающие ошибки и обрабатывать их. Например, ошибку валидации формы можно перехватить на элементе form или соответствующем инпуте и показать пользователю сообщение об этой ошибке. А нераспознанная системная ошибка может всплыть на уровень document. В этом случае пользователь увидит обобщённое сообщение об ошибке.

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

  • Измените что-то и повторите действие. Если пользователь ввёл невалидные данные и не смог отправить форму, благодаря сообщению об ошибке он сможет исправить данные и отправить форму.
  • Попробуйте позже. Пользователь не смог отправить форму из-за ошибки сети. Благодаря сообщению он вернётся через 10 минут и успешно отправит форму.
  • Свяжитесь с нами. Пользователь не смог отправить форму из-за неожиданной ошибки. Благодаря сообщению об ошибке он свяжется со службой поддержки и решит свои задачи.

При обработке ошибок на стороне клиента часто возникает необходимость выбрать между остановкой и продолжением работы приложения. Если ошибка влияет только на часть системы, можно разрешить человеку пользоваться приложением дальше. Если ошибка критическая или она влияет на разные части приложения, можно показать сообщение в модальном окне, которое невозможно закрыть. Также можно заменить контент страницы сообщением об ошибке. Это защитит пользователя от бесполезных попыток выполнить желаемое действие.

Как работать с ошибками в AJAX-запросах и что нужно знать о кодах ответа HTTP

Самый простой и очень эффективный способ сообщить пользователю об ошибке — правильно использовать коды ответов HTTP. Коды статуса HTTP могут самостоятельно дать пользователю достаточно информации о том, почему возникла ошибка запроса, а также подсказать, что делать дальше.

«Ошибочные» коды ответов HTTP объединяются в две группы: ответы 4XX и ответы 5XX. Первые говорят о проблеме с запросом (клиентские ошибки), а вторые — о проблеме с сервером (серверные ошибки). Ниже перечислены самые распространённые «ошибочные» коды статусов HTTP, которые можно получить при работе с веб-приложением:

  • 400 — Bad Request. Обычно этот статус связан с ошибкой ввода, например, если пользователь вводит некорректный адрес электронной почты.
  • 401 — Unauthorized. Этот статус связан с ситуацией, когда пользователь пытается получить доступ к чему-либо без авторизации там, где авторизация требуется. Также этот код ошибки подходит в ситуации, когда пользователь пытается выполнить действие, на которое у него нет прав.
  • 403 — Forbidden. Разница между этим статусом и статусом 400 незначительная. Обычно код 403 говорит о том, что сервер понял запрос, но не может его выполнить. Например, такой статус можно возвращать, если пользователь ввёл номер акционного купона с истекшим сроком действия.
  • 404 — Not Found. Это самый известный из «ошибочных» кодов ответа. Он сообщает, что запрошенный ресурс не найден. Это может произойти из-за некорректного URL, удалённой или перемещённой страницы.
  • 409 — Conflict. В большинстве случаев этот статус говорит о конфликте управления версиями. Например, такое происходит, если пользователь пробует загрузить версию файла, которая старше загруженной ранее версии этого файла. Также этот код может говорить об ограничениях уникальности, например, если пользователь пытается повторно отправить электронное письмо (второй раз нажимает кнопку «Отправить», не дождавшись завершения действия).
  • 500 — Internal Server Error. Этот статус говорит об ошибке, которую можно описать так: «Что-то пошло не так, но мы не знаем, что именно».
  • 503 — Unavailable. Сервер вышел из строя, ошибка может быть запланированной или незапланированной.

Если вы хорошо знаете эти коды, вам будет проще обрабатывать ошибки, которые возникают при работе с AJAX-запросами.

Примечание — Обратите внимание на сервис httpstat.us, он пригодится вам для тестирования реакций на ошибки при разработке фронтенд-приложений.

Как перехватывать ошибки во фронтенд-приложениях

Вы можете определить обработчик глобально с помощью функции window.onerror. В этом случае обработчик переопределит дефолтное поведение браузеров, благодаря чему ваше приложение будет показывать пользователям полезную информацию при возникновении ошибок.

window.onerror = (message, url, lineNumber) => {
  // определяем, знаем ли мы, как обрабатывать ошибку
  if (errorCanBeHandled) {
    // показываем сообщение об ошибке пользователю
    displayErrorMessage(message);
    // возвращаем true и запускаем дефолтную
    // реакцию приложения на фатальные ошибки
    return true;
  } else {
    // запускаем дефолтную обработку ошибок браузером
    return false;
  }
}

Этот подход работает. Но иногда бывает сложно понять точную причину проблемы с помощью выброшенного исключения. При обработке ошибок в AJAX-запросах лучше использовать функцию обработки ошибок библиотеки, которой вы пользуетесь для выполнения запросов. Она позволит определить код ответа и корректно на него среагировать.

Изучайте фронтенд-разработку на Хекслете! Первые курсы в профессии «Фронтенд-программист» доступны бесплатно. Регистрируйтесь и стартуйте в удобное время.

Главное об обработке ошибок во фронтенд-приложениях

Главный факт об обработке ошибок заключается в том, что вы должны их обрабатывать. Любая попытка сообщить пользователю что-то полезное, когда возникает ошибка — отличный ход. Даже информирование с помощью alert() лучше, чем отсутствие информации. Помните, что при проектировании UI вашего приложения нужно учитывать все возможные ситуации, включая различные ошибки.

Адаптированный перевод статьи Front-End Error Handling by Static Apps. Мнение администрации Хекслета может не совпадать с мнением автора оригинальной публикации.

Опубликовано: среда, 29 марта 2023 г. в 09:06

  • JavaScript

В этом руководстве мы погрузимся в обработку ошибок JavaScript, чтобы вы могли выбрасывать исключения, обнаруживать и обрабатывать собственные ошибки.

Опытные разработчики ожидают неожиданного. Если что-то может пойти не так, так оно и будет — обычно в тот момент, когда первый пользователь получает доступ к вашему новому приложению.

Некоторых ошибок веб-приложений можно избежать, например:

  • Хороший редактор или линтер может отлавливать синтаксические ошибки.
  • Хорошая валидация может выявить ошибки пользовательского ввода.
  • Надёжные процессы тестирования могут обнаруживать логические ошибки.

И всё же ошибки остаются. Браузеры могут сбоить или не поддерживать API, который мы используем. Серверы могут дать сбой или слишком долго отвечать. Сетевое соединение может выйти из строя или стать ненадёжным. Проблемы могут быть временными, но мы не может программировать такие проблемы. Однако мы можем предвидеть проблемы, принимать меры по их устранению и повышать отказоустойчивость нашего приложения.

Отображение сообщения об ошибке — крайняя мера

В идеале пользователи никогда не должны видеть сообщения об ошибке.

Мы можем игнорировать незначительные проблемы, такие как невозможность загрузки декоративного изображения. Мы могли бы решить более серьёзные проблемы, такие как сбои сохранения данных Ajax локально и загружая их позже. Ошибка становиться необходимой только тогда, когда пользователь рискует потерять данные, предполагая, что он может что-то с этим сделать.

Поэтому необходимо выявлять ошибки по мере их возникновения и определять наилучшие действия. Вызов и перехват ошибок в приложении JavaScript поначалу может быть пугающим, но, возможно, это проще, чем вы ожидаете.

Как JavaScript обрабатывает ошибки

Когда оператор JavaScript приводит к ошибке, говорят, что он генерирует (выбрасывает) исключение. JavaScript создаёт и выбрасывает объект Error, описывающий ошибку. Мы можем увидеть это в действии на CodePen. Если установить в десятичные разряды (decimal places) отрицательное число, мы увидим сообщение об ошибке в консоли внизу. (Обратите внимание, что мы не встраиваем CodePen в это руководство, потому что нужно иметь возможно видеть вывод консоли, чтобы этот пример имел смысл)

Результат не обновиться, и мы увидим сообщение RangeError в консоли. Следующая функция выдаёт ошибку, когда dp имеет отрицательно значение:

// division calculation
function divide(v1, v2, dp) {

return (v1 / v2).toFixed(dp);

}

После выдачи ошибки интерпретатор JavaScript проверяет наличие кода обработки исключений. В функции Division() ничего нет, поэтому она проверяет вызывающую функцию:

// show result of division
function showResult() {

result.value = divide(
parseFloat(num1.value),
parseFloat(num2.value),
parseFloat(dp.value)
);

}

Интерпретатор повторяет процесс для каждой функции в стеке вызовов, пока не произойдёт одно из следующих событий:

  • Он находит обработчик исключений.
  • Он достигает верхнего уровня кода (что приводит к завершению программы и отображению ошибки в консоли, как показано в примере на CodePen выше).

Перехват исключений

Мы можем добавить обработчик исключений в функцию divide() с помощью блока try...catch:

// division calculation
function divide(v1, v2, dp) {
try {
return (v1 / v2).toFixed(dp);
}
catch(e) {
console.log(`
error name :
${ e.name }
error message:
${ e.message }
`
);
return 'ERROR';
}
}

Функция выполняет код в блоке try {}, но при возникновении исключения выполняется блок catch {} и получает выброшенный объект ошибки. Как и прежде, попробуйте в decimal places установить отрицательное число в этой демонстрации CodePen.

Теперь result показывает ERROR. Консоль показывает имя ошибки и сообщение, но это выводится оператором console.log и не завершает работу программы.

Примечание: эта демонстрация блока try...catch излишняя для базовой функции, такой как divide(). Как мы увидим ниже, проще убедиться, что dp равен нулю или больше.

Можно определить не обязательный блок finally {}, если требуется, чтобы код запускался при выполнении кода try или catch:

function divide(v1, v2, dp) {
try {
return (v1 / v2).toFixed(dp);
}
catch(e) {
return 'ERROR';
}
finally {
console.log('done');
}
}

В консоль выведется done, независимо от того, успешно ли выполнено вычисление или возникла ошибка. Блок finally обычно выполняет действия, которые в противном случае нам пришлось бы повторять как в блоке try, так и в блоке catch. Например, отмену вызова API или закрытие соединения с базой данных.

Для блока try требуется либо блок catch, либо блок finally, либо и то и другое. Обратите внимание, что когда блок finally содержит оператор return, это значение становится возвращаемым значением для всей функции; другие операторы в блоках try или catch игнорируются.

Вложенные обработчики исключений

Что произойдёт, если мы добавим обработчик исключений к вызывающей функции showResult()?

// show result of division
function showResult() {

try {
result.value = divide(
parseFloat(num1.value),
parseFloat(num2.value),
parseFloat(dp.value)
);
}
catch(e) {
result.value = 'FAIL!';
}

}

Ответ… ничего! Блок catch никогда не выполняется, потому что в функции divide() блок catch обрабатывает ошибку.

Тем не менее мы могли бы программно генерировать новый объект Error в divide() и при желании передать исходную ошибку в свойстве cause второго аргумента:

function divide(v1, v2, dp) {
try {
return (v1 / v2).toFixed(dp);
}
catch(e) {
throw new Error('ERROR', { cause: e });
}
}

Это вызовет блок catch в вызывающей функции:

// show result of division
function showResult() {

try {
//...
}
catch(e) {
console.log( e.message ); // ERROR
console.log( e.cause.name ); // RangeError
result.value = 'FAIL!';
}
}

Стандартные типы ошибок JavaScript

Когда возникает исключение, JavaScript создаёт и выдаёт объект, описывающий ошибку, используя один из следующих типов.

SyntaxError

Ошибка, возникающая из-за синтаксически недопустимого кода, такого как отсутствующая скобка:

if condition) { // SyntaxError
console.log('condition is true');
}

Примечание: такие языки, как C++ и Java, сообщают об ошибках синтаксиса во время компиляции. JavaScript — интерпретируемый язык, поэтому синтаксические ошибки не выявляются до тех пор, пока код не запустится. Любой хороший редактор кода или линтер могут обнаружить синтаксические ошибки до того, как мы попытаемся запустить код.

ReferenceError

Ошибка при доступе к несуществующей переменной:

function inc() {
value++; // ReferenceError
}

Опять, хороший редактор кода или линтер могут обнаружить эту проблему.

TypeError

Ошибка возникает, когда значение не соответствует ожидаемому типу, например, при вызове несуществующего метода объекта:

const obj = {};
obj.missingMethod(); // TypeError

RangeError

Ошибка возникает, когда значение не входит в набор или диапазон допустимых значений. Используемый выше метод toFixed() генерирует эту ошибку, потому что он ожидает значение от 0 до 100:

const n = 123.456;
console.log( n.toFixed(-1) ); // RangeError

URIError

Ошибка выдаваемая функциями обработки URI, такими как encodeURI() и decodeURI(), при обнаружении неправильных URI:

const u = decodeURIComponent('%'); // URIError

EvalError

Ошибка возникающая при передаче строки, содержащей не валидный JavaScript код, в функцию eval():

eval('console.logg x;'); // EvalError

Примечание: пожалуйста, не используйте eval()! Выполнение произвольного кода, содержащегося в строке, возможно, созданной на основе пользовательского ввода, слишком опасно!

AggregateError

Ошибка возникает, когда несколько ошибок объединены в одну ошибку. Обычно возникает при вызове такой операции, как Promise.all(), которая возвращает результаты нескольких промисов.

InternalError

Нестандартная ошибка (только в Firefox) возникает при возникновении внутренней ошибки движка JavaScript. Обычно это результат того, что что-то занимает слишком много памяти, например, большой массив или слишком много рекурсии.

Error

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

Генерация/выбрасывание собственных исключений

Мы можем использовать throw для генерации/выбрасывания собственных исключений, когда возникает ошибка — или должна произойти. Например:

  • нашей функции не передаются валидные параметры
  • ajax-запрос не возвращает ожидаемые данные
  • обновление DOM завершается ошибкой, поскольку узел не существует

Оператор throw фактически принимает любое значение или объект. Например:

throw 'A simple error string';
throw 42;
throw true;
throw { message: 'An error', name: 'MyError' };

Исключения генерируются для каждой функции в стеке вызовов до тех пор, пока они не будут перехвачены обработчиком исключений (catch). Однако на практике мы хотим создать и сгенерировать объект Error, чтобы он действовал идентично стандартным ошибкам, выдаваемым JavaScript.

Можно создать общий объект Error, передав необязательное сообщение конструктору:

throw new Error('An error has occurred');

Так же Error можно использовать как функцию, без new. Она возвращает объект Error, идентичный приведённому выше:

throw Error('An error has occurred');

При желании можно передать имя файла и номер строки в качестве второго и третьего параметров:

throw new Error('An error has occurred', 'script.js', 99);

В этом редко возникает необходимость, так как по умолчанию они относятся к файлу и строке, где мы вызвали объект Error. (Также их сложно поддерживать, поскольку наши файлы меняются!)

Мы можем определить общие объекты Error, но по возможности следует использовать стандартный тип Error. Например:

throw new RangeError('Decimal places must be 0 or greater');

Все объекты Error имеют следующие свойства, которые можно проверить в блоке catch:

  • .name: имя типа ошибки, например Error или RangeError.
  • .message: сообщение об ошибке.

В Firefox поддерживаются следующие нестандартные свойства:

  • .fileName: файл, в котором произошла ошибка.
  • .lineNumber: номер строки, в которой произошла ошибка.
  • .columnNumber: номер столбца, в котором произошла ошибка.
  • .stack: трассировка стека со списком вызовов функций, сделанных до возникновения ошибки.

Мы можем изменить функцию divide() так, чтобы она вызывала ошибку RangeError, когда количество знаков после запятой не является числом, меньше нуля и больше восьми:

// division calculation
function divide(v1, v2, dp) {

if (isNaN(dp) || dp < 0 || dp > 8) {
throw new RangeError('Decimal places must be between 0 and 8');
}

return (v1 / v2).toFixed(dp);
}

Точно так же мы могли бы выдать Error или TypeError, когда значения делимого не является числом, чтобы предотвратить результат NaN:

  if (isNaN(v1)) {
throw new TypeError('Dividend must be a number');
}

Также можно обрабатывать делитель, который не является числом или равен нулю. JavaScript возвращает Infinity при делении на ноль, но это может запутать пользователя. Вместо того чтобы вызывать общую ошибку, мы могли бы создать собственный тип ошибки DivByZeroError:

// new DivByZeroError Error type
class DivByZeroError extends Error {
constructor(message) {
super(message);
this.name = 'DivByZeroError';
}
}

Затем вызывать/выбрасывать его подобным образом:

if (isNaN(v2) || !v2) {
throw new DivByZeroError('Divisor must be a non-zero number');
}

Теперь добавьте блок try...catch к вызывающей функции showResult(). Он сможет получить тип любой ошибки и отреагировать соответствующим образом — в данном случае, выводя сообщение об ошибке:

// show result of division
function showResult() {

try {
result.value = divide(
parseFloat(num1.value),
parseFloat(num2.value),
parseFloat(dp.value)
);
errmsg.textContent = '';
}
catch (e) {
result.value = 'ERROR';
errmsg.textContent = e.message;
console.log( e.name );
}

}

Попробуйте ввести недопустимые нечисловые, нулевые и отрицательные значения в демонстрации на CodePen.

Окончательная версия функции divide() проверяет все входящие значения и при необходимости выдаёт соответствующую ошибку:

// division calculation
function divide(v1, v2, dp) {

if (isNaN(v1)) {
throw new TypeError('Dividend must be a number');
}

if (isNaN(v2) || !v2) {
throw new DivByZeroError('Divisor must be a non-zero number');
}

if (isNaN(dp) || dp < 0 || dp > 8) {
throw new RangeError('Decimal places must be between 0 and 8');
}

return (v1 / v2).toFixed(dp);
}

Больше нет необходимости размещать блок try...catch вокруг финального return, так как он никогда не должен генерировать ошибку. Если бы это произошло, JavaScript сгенерировал бы свою собственную ошибку и обработал бы её блоком catch в showResult()/

Ошибки асинхронной функции

Мы не можем перехватывать исключения, генерируемые асинхронными функциями на основе обратного вызова, потому что после завершения выполнения блока try...catch выдаётся ошибка. Этот код выглядит правильно, но блок catch никогда не выполнится, и через секунду консоль отобразит сообщение Uncaught Error:

function asyncError(delay = 1000) {

setTimeout(() => {
throw new Error('I am never caught!');
}, delay);

}

try {
asyncError();
}
catch(e) {
console.error('This will never run');
}

Соглашение, принятое в большинстве фреймворков и серверных сред выполнения, таких как Node.js, заключается в том, чтобы возвращать ошибку в качестве первого параметра функции обратного вызова. Это не приведёт к возникновению исключения, хотя при необходимости мы можем вручную сгенерировать ошибку:

function asyncError(delay = 1000, callback) {

setTimeout(() => {
callback('This is an error message');
}, delay);

}

asyncError(1000, e => {

if (e) {
throw new Error(`error: ${ e }`);
}

});

Ошибки на основе промисов

Обратные вызовы могут стать громоздкими, поэтому при написании асинхронного кода предпочтительнее использовать промисы. При возникновении ошибки метод reject() промиса может вернуть новый объект Error или любое другое значение:

function wait(delay = 1000) {

return new Promise((resolve, reject) => {

if (isNaN(delay) || delay < 0) {
reject( new TypeError('Invalid delay') );
}
else {
setTimeout(() => {
resolve(`waited ${ delay } ms`);
}, delay);
}

})

}

Примечание: функции должны быть либо 100% синхронными, либо 100% асинхронными. Вот почему необходимо проверять значение delay внутри возвращаемого промиса. Если бы мы проверили значение delay и выдали ошибку перед возвратом промиса, функция стала бы синхронной при возникновении ошибки.

Метод Promise.catch() выполняется при передаче недопустимого параметра delay и получает возвращённый объект Error:

// invalid delay value passed
wait('INVALID')
.then( res => console.log( res ))
.catch( e => console.error( e.message ) )
.finally( () => console.log('complete') );

Я считаю цепочки промисов немного сложными для чтения. К счастью, мы можем использовать await для вызова любой функции, возвращающей промис. Это должно происходить внутри асинхронной функции, но мы можем перехватывать ошибки с помощью стандартного блока try...catch.

Следующая (вызываемая немедленно) асинхронная функция функционально идентична цепочке промисов выше:

(async () => {

try {
console.log( await wait('INVALID') );
}
catch (e) {
console.error( e.message );
}
finally {
console.log('complete');
}

})();

Исключительная обработка исключения

Выбрасывать объекты Error и обрабатывать исключения в JavaScript легко:

try {
throw new Error('I am an error!');
}
catch (e) {
console.log(`error ${ e.message }`)
}

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

Дополнительная информация:

  • MDN Порядок выполнения и обработка ошибок
  • MDN try…catch
  • MDN Error

Корректная обработка ошибки 404

404 ошибка возникает в браузере достаточно часто и происходит в случае, когда сервер не нашел страницу, на которую ведет ссылка. Этому может быть две причины: либо указанной страницы не существует либо пользователь перешел на сайт по некорректной ссылке.  В большинстве случаев, когда посетитель попадает на стандартную страницу ошибки 404, он чувствует себя в тупике и не находит лучшего решения, чем закрыть страницу и продолжить поиск.  А это в свою очередь отрицательно сказывается на посещаемости сайта.

Правильная обработка страницы 404 ошибки

  1. Сообщение «Страница не найдена» стандартной обработки ошибки 404 не несет никакой пользы для посетителя, что ухудшает впечатление пользователя о сайте.
  2. Если в поисках информации пользователь наткнулся на битую ссылку, то он вынужден возвращаться на главную и начинать поиск заново.

В идеальной ситуации страница с ошибкой 404 не должна возникать вообще,  но как бы с этим не боролись, сбои порой случаются, а правильно обработанная ошибка 404 дает возможность извиниться за них перед пользователем и предоставить удобный способ продолжить пользование сайтом. 

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

Как сделать страницу ошибки 404 максимально полезной?

  1. Дизайн страницы ошибки не должен отличаться от остального дизайна веб-сайта, включая логотип,  цветовую схему, навигацию и т.п.
  2. Сделайте страницу информативной для пользователя. Для этого на нее можно пометить  следующие элементы:
    • ссылку на главную страницу сайта
    • форму поиска по сайту
    • самые популярные страницы сайта(топ-10)
    • ссылку на карту сайта
  3. Простая, а порой даже забавная обработка страницы 404 ошибки, поможет задержать на сайте пользователя. Есть много сайтов тому подтверждением
  4. Сообщите об ошибке в дружелюбной манере. «Упс! Извините, мы не можем найти данную страницу!» звучит приятнее, чем «Ошибка 404 – страница не найдена»

Как реализуется обработка 404 ошибки?

Во-первых, необходимо запретить индексацию данной страницы  поисковыми роботами. Для этого необходимо убедиться, что возвращается правильный http статус ошибки, тогда поисковики ее проигнорируют. Но для надежности лучше запретить  страницу в файле robots.txt , добавив в него всего одну строку:

Disallow: /404.html

Во-вторых,  чтобы отображалась  собственная страница 404 ошибки в файле .htaccess (для сервера Apache) необходимо прописать следующее:

ErrorDocument  404 / 404-error.html

где 404-error.html и есть специально созданная страница-обработчик ошибки.

Почему не стоит перенаправлять пользователя на другую (например, главную)  страницу сайта?  Перенаправление приводит к замешательству пользователя, так как выдается не то, что он искал – необходимо информировать об ошибке. Кроме того, если ответ страницы будет не 404, то это наплодит большое число неинформативных страниц в поисковых системах.

Как бороться с возникновением ошибки 404 на сайте?

Для этого нужно периодически просматривать состояние внутренних ссылок. Лучше всего использовать специальное ПО, позволяющее обнаружить битые ссылки.  Если же такие ссылки находятся на сторонних ресурсах, то обращение к вебмастеру с просьбой исправить не всегда помогает. Здесь можно воспользоваться отдельными скриптами для перенаправления на работающие страницы сайта. 

Грамотная обработка 404 ошибки поможет вам удержать пользователей на сайте и не отпугнуть их от дальнейших действий.

Вот несколько нестандартных решений оформления страницы обработки ошибки 404 различными ресурсами:

Сайт eu.blizzard.com

Корректная обработка ошибки 404 различными ресурсами

  1. Сообщение «Страница не найдена» стандартной обработки ошибки 404 не несет никакой пользы для посетителя, что ухудшает впечатление пользователя о сайте.
  2. Если в поисках информации пользователь наткнулся на битую ссылку, то он вынужден возвращаться на главную и начинать поиск заново.

В идеальной ситуации страница с ошибкой 404 не должна возникать вообще,  но как бы с этим не боролись, сбои порой случаются, а правильно обработанная ошибка 404 дает возможность извиниться за них перед пользователем и предоставить удобный способ продолжить пользование сайтом. 

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

Как сделать страницу ошибки 404 максимально полезной?

  1. Дизайн страницы ошибки не должен отличаться от остального дизайна веб-сайта, включая логотип,  цветовую схему, навигацию и т.п.
  2. Сделайте страницу информативной для пользователя. Для этого на нее можно пометить  следующие элементы:
    • ссылку на главную страницу сайта
    • форму поиска по сайту
    • самые популярные страницы сайта(топ-10)
    • ссылку на карту сайта
  3. Простая, а порой даже забавная обработка страницы 404 ошибки, поможет задержать на сайте пользователя. Есть много сайтов тому подтверждением
  4. Сообщите об ошибке в дружелюбной манере. «Упс! Извините, мы не можем найти данную страницу!» звучит приятнее, чем «Ошибка 404 – страница не найдена»

Как реализуется обработка 404 ошибки?

Во-первых, необходимо запретить индексацию данной страницы  поисковыми роботами. Для этого необходимо убедиться, что возвращается правильный http статус ошибки, тогда поисковики ее проигнорируют. Но для надежности лучше запретить  страницу в файле robots.txt , добавив в него всего одну строку:

Disallow: /404.html

Во-вторых,  чтобы отображалась  собственная страница 404 ошибки в файле .htaccess (для сервера Apache) необходимо прописать следующее:

ErrorDocument  404 / 404-error.html

где 404-error.html и есть специально созданная страница-обработчик ошибки.

Почему не стоит перенаправлять пользователя на другую (например, главную)  страницу сайта?  Перенаправление приводит к замешательству пользователя, так как выдается не то, что он искал – необходимо информировать об ошибке. Кроме того, если ответ страницы будет не 404, то это наплодит большое число неинформативных страниц в поисковых системах.

Как бороться с возникновением ошибки 404 на сайте?

Для этого нужно периодически просматривать состояние внутренних ссылок. Лучше всего использовать специальное ПО, позволяющее обнаружить битые ссылки.  Если же такие ссылки находятся на сторонних ресурсах, то обращение к вебмастеру с просьбой исправить не всегда помогает. Здесь можно воспользоваться отдельными скриптами для перенаправления на работающие страницы сайта. 

Грамотная обработка 404 ошибки поможет вам удержать пользователей на сайте и не отпугнуть их от дальнейших действий.

Вот несколько нестандартных решений оформления страницы обработки ошибки 404 различными ресурсами:

Сайт eu.blizzard.com

Корректная обработка ошибки 404 различными ресурсами

 Сайт chrisjennings.com

404 ошибка - сайт не найден

 Сайт jhuskisson.com

Страница не найдена, обработка 404 ошибки

 Сайт jhuskisson.com

Страница не найдена, обработка 404 ошибки

Вернуться назад

  • 10.07.2012
  • веб-дизайн

Обработка 404 ошибки

При попытке входа на сайт может возникнуть несколько ошибок, которые не дадут вам этого сделать. Они могут быть вызваны огромным количеством разнообразных причин. Одной из самых часто встречающихся является ошибка 404. Из-за нее чаще всего снижается производительность сайтов. Особенно важно настроить на сайте вывод специальной страницы вместо обычного извещение об ошибке 404.

Часто случается так, что ошибки подобного типа происходят регулярно. Это может быть вызвано несколькими факторами.

  • Отсутствует файл favicon.ico, который запрашивается браузером при каждом входе на страницу. Этот файл требуется, чтобы появилось изображение перед URL страницы в адресной строке браузера.
  • Отсутствует файл robots.txt. Он создается для роботов поисковиков, которые запрашивают его, когда выполняются запросы сайта. Это позволяет более грамотно распределять ресурсы поискового робота, указывая ему на страницы какие следует обойти, а какие нет.
  • Отсутствует файл javascript. Если в html-коде присутствует ссылка на данный файл, а сам файл отсутствует, то возникнет ошибка.

Для того, чтобы решить все проблемы подобного типа вам требуется проверить, что запрашиваемые файлы действительно находятся на вашем сервере. После устранения данной проблемы, скорость загрузки сайта может возрасти в разы.
Важно понимать, что обработкой 404 ошибок пользователь должен заниматься самостоятельно, ведь обработка этих ошибок не включена в настройки системы.

Этапы обработки ошибки 404

  • Пользователь получает ошибку сервера.
  • Уточняется, какая именно ошибка произошла.
  • В случае 404 ошибки, происходит ее обработка, которая выполняет следующие действия:
  • устанавливает кода ошибки 404.
  • создает документ из шаблона.
  • удаляет код ошибки с сервера.

Также проводится проверка обработки страниц 404.

Очень часто сервером хостинга является Apache, в таком случае, в файл .htaccess следует добавить код:

ErrorDocument 404 /404.html

Где 404.html — это страница, сообщающая об ошибке, а .htaccess – это файл, содержащий правила для работы сервера.

В ситуации, когда сервером является IIS, для устранения ошибки нужно в разделе настроек менеджера Error Pages найти файл web.config и прописать код обработки ошибок:

error statusCode="404" prefixLanguageFilePath="" path="/404.php" responseMode="ExecuteURL" /

Когда возникает ошибка 404, очень важно сообщить о ней и о ее причине посетителям. Ваши пользователи должны понимать, что попали на несуществующую страницу, а также о том, что они могут вернуться к главной странице ресурса. Причем ссылка для продолжения навигации по сайту должна быть размещена на странице-обработчике 404 страницы, иначе ваши посетители могут просто покинуть сайт и уйти на работающий сайт вашего конкурента. При этом важно не создавать редирект на отдельную 404-ю страницу, а реализовать обработку таким образом, чтобы непосредственно сама несуществующая страница имела код ответа сервера 404. Именно на этой странице вы должны дать пользователю информацию о произошедшем сбое и о том, что он может продолжить навигацию по сайту.

Отслеживать появление и обработку ошибок 404 вы можете в файле error_log, он покажет вам, каких файлов не хватает на сайте. Это поможет избежать ссылок на такие несуществующие файлы.

Стоит отметить, что описанная выше директива ErrorDocument способна обрабатывать не только 404 ошибку, но и все остальные.

Сергей Коваль

Опубликовано: 9 ноября 2021

Ошибка 404: проверка, настройка и оформление

Разбираемся, почему возникает ошибка 404 и как обратить ее появление в свою пользу

Иногда переходя по ссылке из различных ресурсов или даже из поисковой системы пользователь вместо страницы вашего сайта может увидеть сообщение браузера: 404 ошибка. В этом случае он просто закрывает вкладку и забывает о намерении посетить ваш сервис.

Однако вы можете изменить его решение и обратить ошибку 404 в свою пользу. Как это сделать, узнаете из нашей статьи.

Причины возникновения ошибки 404

Ошибка 404 – это не результат неправильной работы сайта. Ее причинами могут быть:

  • ошибки и опечатки при ручном вводе или копировании адреса;
  • нарушение внутренней перелинковки;
  • переход по ссылке на удаленную или скрытую страницу;
  • переход по ссылке на страницу, к которой у пользователя нет доступа.

Ошибка 404 довольно неприятное явление для владельца сайта, она имеет ряд негативных последствий:

  • Пользователи покидают ваш сайт так и не узнав о ваших услугах, товарах или других возможностях, которые он предлагает.
  • Ваш сайт теряет посещения, отчего страдает статистика, а значит и ранжирование сайта.
  • У пользователей складывается негативное впечатление о вашем ресурсе.

К сожалению, возникновение ошибки 404 в большинстве случаев зависит не от владельца сайта. Однако есть способы, как сделать страницу ошибки полезной для себя и своего ресурса.

Как обработать страницу ошибки 404?

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

Позаботьтесь об оформлении страницы. Она должна соответствовать сайту в целом и свидетельствовать о серьезности и солидности ресурса. Разместите на странице свои логотипы, оформите в единой с сайтом цветовой схеме.

Сделайте страницу полезной для посетителя, то есть разместите на ней элементы, которые помогут ему перейти к полезной информации. Для этого на страницу ошибки 404 следует поместить:

  • ссылку, ведущую на главную страницу вашего сайта, в каталог или к другой полезной по вашему мнению информации;
  • форму поиска для того, чтоб посетители могли быстро найти любые данные, введя соответствующий запрос;
  • ссылки для перехода на самые популярные страницы вашего сервиса, например, оформив их каталогом или в виде рейтинга ТОП-10;
  • ссылку на карту сайта;
  • рекламную и полезную информацию, например, объявление о скидках, новых товарах или рубриках.

Довольно интересными идеями, которые привлекут внимание пользователя и вызовут интерес, станут необычные или смешные картинки. Проявите креатив в этом вопросе. Также внимание привлекают и сообщения в нестандартной, юмористической форме. Например: Ой, мы не можем найти эту страницу, но раз уж вы здесь, посетите нашу распродажу. Или: Упс, что-то пошло не так, может вам будет интересна следующая информация?

Обработка ошибки 404

Чтобы ошибка 404 не отразилась на популярности сайта, ее следует настроить.

Для начала отключите индексацию данной страницы поисковыми роботами, а лучше запретите ее. Тогда поисковики будут игнорировать эту страницу вашего сайта и не направлять посетителей на нее по недоразумению. Чтобы запретить страницу, найдите файл robots.txt и добавьте в него всего одну строчку: Disallow: /404.html.

Далее следует заменить стандартную страницу на собственную уникальную разработку. Для этого найдите файл с расширением .htaccess и внесите в него следующую строку: ErrorDocument 404 / 404-error.html. Где 404-error.html – это и будет URL созданной вами оригинальной страницы для обработки ошибки.

А вот подменять страницу ошибки переходом на главную страницу вашего ресурса не стоит. Не факт, что на ней находится искомая пользователем информация. В результате посетитель решит, что у поисковика произошел какой-то сбой и закроет ваш сайт. Лучше честно признаться, что страницы уже нет или не существовало вовсе, и предложить пользователю самостоятельно найти нужную информацию.

Как минимизировать вероятность возникновения ошибки 404

Как мы уже выяснили, ошибка 404 может возникать и в результате сбоев на сайте. Чтоб снизить эту вероятность, регулярно выполняйте мониторинг внутренних ссылок. Если удаляете или скрываете от пользователей какую-либо страницу или раздел, позаботьтесь, чтоб на них не было переходов с других страниц.

Для обнаружения битых ссылок может пригодиться и специальное программное обеспечение.

Если битые или устаревшие ссылки расположены на других ресурсах, обратитесь с просьбой об их замене к вебмастеру. Если это невозможно, воспользуйтесь скриптами, перенаправляющими пользователей на нужный раздел.

Запомните, от ошибки 404 не защищен ни один ресурс. Однако грамотная ее обработка поможет удержать посетителей и не скажется на статистике и ранжировании сайта.

Ошибка с кодом 404 появляется у пользователя при попытке открыть страницу, которой не существует на сайте. Виной тому может быть битая ссылка или просто неверно введенный адрес, однако сама проблема появляется довольно часто, так что вопрос, как исправить ошибку 404, возникает как у владельцев сайтов, так и у обычных пользователей.

Причины появления ошибки с кодом 404

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

  1. Неверный адрес. Чаще всего появляется, когда юзер вручную вводит адрес страницы или пытается перейти в несуществующий раздел сайта.
  2. Удаленная страница. Иногда страницы удаляются с сайта его владельцем, однако из поисковика они не пропадают еще некоторое время. Кроме того, прямые ссылки могут храниться в закладках у некоторых пользователей. Соответственно, при переходе и появится соответствующее уведомление об ошибке сервера.
  3. Битая ссылка. Такая страница когда-то действительно существовала, однако сбой произошел при перелинковке, и теперь ссылка ведет в никуда, что и влияет на возникновение проблемы.
  4. Проблемы функционирования сервера. Последняя причина появляется крайне редко и связана со сбоями на сервере, где размещен сайт.

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

Комьюнити теперь в Телеграм

Подпишитесь и будьте в курсе последних IT-новостей

Подписаться

Поиск битых ссылок на сайте

Если вы являетесь владельцем сайта, можно проверить наличие битых ссылок и удаленных страниц при помощи специальных онлайн-сервисов или программ. Существует несколько популярных инструментов, о которых и пойдет речь далее.

Яндекс.Вебмастер

Проще всего использовать сайт Яндекс.Вебмастер. Потребуется авторизоваться в сервисе и добавить собственный сайт, выполнив простые инструкции, которые будут отображаться на экране. После этого выполните такую последовательность действий:

Проверка сайта через Яндекс.Вебмастер при решении проблем с ошибкой 404

  1. Через левое меню откройте раздел «Индексирование».
  2. Там вас интересует категория «Страницы в поиске».
  3. Снизу перейдите на вкладку «Исключительные страницы».
  4. Задайте фильтрацию, чтобы сначала отображались результаты, где присутствует «ошибка 404: страница не найдена».

Google Search Console

Онлайн-сервис от известной компании Google Search Console функционирует примерно по такому же принципу, а для поиска проблемных страниц пользователю потребуется выполнить следующие действия:

Проверка сайта через Google Search Console при решении проблем с ошибкой 404

  1. Выполните вход и добавьте свой сайт. 
  2. Откройте раздел «Сканирование».
  3. Перейдите к категории «Ошибки сканирования».
  4. Используйте фильтр или самостоятельно ознакомьтесь с присутствующими ошибками.

Screaming Frog

Screaming Frog специализированное программное обеспечение, подходящее для сканирования сайтов. Если приведенные выше онлайн-сервисы вам не подошли, скачайте это решение с официального сайта, подключите к нему ваш сайт и произведите сканирование.

Проверка сайта через Screaming Frog при решении проблем с ошибкой 404

Благодаря данному инструменту у вас получится легко обнаружить все проблемные страницы, в том числе и страницы с другими ошибками сервера. 

Исправление ошибки 404

С принципом обнаружения проблемных страниц все понятно, однако как исправить ошибку 404 Page Not Found? Здесь все зависит непосредственно от возникшей ситуации, а также того, используется ли на сайте CMS. Давайте по очереди разберем каждую ситуацию.

При использовании CMS

Сейчас многие сайты создаются при помощи CMS. Это может быть WordPress, Joomla, 1С-Битрикс или какие-либо другие системы управления содержимым. Если проблема возникает на таком сайте, а на экране при переходе к сайту вы видите следующее изображение, значит, потребуется выполнить ряд действий для исправления неполадки.
Появление ошибки 404 от хостинга при использовании CMSВ первую очередь перейдите в корневую папку сайта и создайте там документ с расширением .htaccess. Он должен быть текстовым. Его содержимое зависит от конкретной используемой CMS. То есть необходимо прописать стандартные директивы, которые вы можете найти на официальном сайте reg.ru. Другие полезные материалы по настройке htaccess вы найдете тут и тут. 

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

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

Появление ошибки 404 от CMS

В таких ситуациях вам необходимо проверить каждое название ссылки на правильность ввода, а также убедиться в том, что установленные SEO-плагины функционируют нормально и не влияют на проблемную генерацию ссылок. После проверки каждой ссылки ошибка http 404 должна исчезнуть.

Без использования CMS

Иногда сайты функционируют и без использования CMS. В таких ситуациях метод решения будет другим, поскольку, скорее всего, причина возникновения неполадки связана с отсутствием файлов в папке сайта или их неправильном расположении.
Появление ошибки 404 на сайте без использования CMSДля начала откройте корневую папку с файлом через панель хостинга и убедитесь в наличии всех необходимых элементов (например, html-страниц). В случае отсутствия потребуется загрузить их вручную и проверить, исчезла ли ошибка. Если же она сохранилась или файлы изначально находились в правильном месте, обращайтесь напрямую в техническую поддержку хостинга.

Только что мы разобрались с тем, что значит ошибка 404 Not Found, а также рассмотрели методы ее решения для разных ситуаций. Владельцу сайта нужно внимательно подойти к этому вопросу и всегда быть аккуратным при составлении адресов ссылок. От обычного же юзера требуется только соблюдать правильность ввода URL, если речь идет о ручном наборе в адресной строке браузера.

Ошибка 404 — это стандартный код ответа HTTP, который говорит о том, что страница не может быть найдена. Ошибка 404 означает, что, хотя сам сервер ответил на запрос, конкретная страница, показывающая ошибку, оказалась недоступна.

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

ошибка 404

Причины возникновения ошибок 404

Как правило, появление такой ошибки вызывают:

  • перемещение страницы сайта без перенаправления старого URL-адреса на новый;
  • ссылка на страницу, которая уже удалена;
  • неправильное написание URL в адресной строке или ссылке;
  • появление большого количества ошибок 404 может быть вызвано вирусной атакой (например, размещением на сайте дорвеев).

Как ошибки 404 влияют на ранжирование сайта?

Хотя ошибки 404 сами по себе не являются фактором ранжирования, они могут отрицательно повлиять на SEO-продвижение.

Даже единичные битые ссылки снижают поведенческие характеристики сайта, вызывая уход посетителей и рост показателя отказов. Это является негативным сигналом для поисковых систем. Официальные источники Яндекса и Google рекомендуют избегать неработающих ссылок на сайте. Большое количество таких ошибок негативно влияет на его ранжирование. Чем больше неработающих ссылок на вашем сайте, тем сложнее будет Google и другим поисковым системам сканировать сайт. Поисковые системы считают, что за состоянием такого веб-ресурса никто не следит, и понижают его позиции в результатах поиска. Страницы, отдающие ответ с ошибкой 404, исключаются из индекса (базы данных поисковой системы).

Полностью избежать возникновения ошибки 404 невозможно, она может возникнуть на любом сайте — где-то поменяли адрес, удалили какой-то раздел или страницу, а ссылки на них осталась. Например, это может произойти при удалении карточек товаров в интернет-магазинах.

Чтобы наличие на сайте таких страниц не привело к ухудшению ранжирования ресурса, они должны быть правильно настроены — давать код ответа 404 (Not Found). Именно такой ответ сообщает поисковому роботу, что данной страницы не существует, и предотвращает ее индексирование.

Страница может выглядеть нормальной, но при этом давать ответ 404 или наоборот — уже несуществующая страница возвращает ответ 200 (страница доступна). Это вводит в заблуждение поисковые системы.

Если вместо кода 404 будет даваться ответ 200, в индекс попадет множество бесполезных страниц, которые будут сканироваться поисковым роботом вместо страниц с качественным контентом.

Лучший способ сделать так, чтобы посетители не попадали на страницу 404 — регулярно проверять наличие на сайте так называемых «битых» ссылок, которые ведут на несуществующие страницы. Это касается как внутренней перелинковки, так и внешних ссылок на другие ресурсы.

Как обнаружить ошибки 404 с помощью сервиса Labrika

Отчет «Ошибки 4хх» вы сможете найти в левом боковом меню в разделе «Технический аудит».

ошибка 404

Отчет содержит:

  • URL-адрес страницы, которая отдала код ошибки 4xx.
  • Информацию о коде ответа страницы.
  • Кнопку для проверки HTTP-статуса — при нажатии на неё откроется сервис проверки HTTP-заголовков страниц.

ошибка 404

  • Список страниц, с которых ведут ссылки на данную страницу с ошибкой 404 — открывается при нажатии на кнопку «Показать».

ошибка 404

Как исправить ошибки 404?

Необходимо удалить ссылки на несуществующие страницы или восстановить работоспособность этих страниц. В первую очередь исправляйте важные страницы с ошибками 404 — карточки товаров, страницы услуг и контактов и т. д.

  • Самый простой способ исправить ошибки 404 – перенаправление. Если контент переместился, настройте перенаправление на новый адрес. В случае удаления содержимого перенаправьте пользователей на другую релевантную страницу на вашем сайте. По возможности делайте перенаправление на что-то актуальное и полезное, а не просто отправляйте обратно на свою домашнюю страницу.
  • Восстановление страницы. Если удаленная страница продолжает пользоваться высоким спросом, и нет другого подходящего контента для переадресации пользователей, рассмотрите возможность восстановления исходной страницы.
  • Исправление ссылок. При наличии на вашем сайте неработающих ссылок отредактируйте их так, чтобы ссылки указывали на правильный URL-адрес.
  • Если вы безвозвратно удалили содержимое страницы и не планируете заменять его другим связанным контентом, позвольте старому URL-адресу возвращать код ответа 404.
  • Создайте информативную страницу ошибки 404. Она должна сообщить пользователю, что документ по этому адресу отсутствует, и предоставить возможность перейти на другие разделы сайта. Иначе посетители могут уйти с веб-ресурса, что приведет к потере трафика и ухудшению поведенческих факторов.

ошибка 404

Более подробно об оформлении страницы 404-й ошибки вы можете прочитать в отдельной статье нашего сайта.

Эта ошибка встречается довольно часто, и она является самой распространенной причиной снижения производительности сайтов. Проблема становится серьезной для тех сайтов, где вместо стандартного сообщения об 404 ошибке обработка пользователю показывает специальную страница. Такой вариант безвреден, пока количество ошибок будет небольшим по сравнению с количеством запросов.

Довольно часто встречаются случаи, когда ошибки 404 приобретают характер систематических ошибок:

  • Отсутствие файла favicon.ico. Когда вы открываете любую страницу, браузер запрашивает изображение с именем favicon.ico, которое отображается в адресной строке перед адресом страницы.

  • Отсутствие файла robots.txt. Данный файл запрашивают роботы поисковиков регулярно перед каждым выполнением запросов сайта.

  • Отсутствующий javascript файл, когда на этот файл имеется ссылка в шаблоне HTML-кода сайта.

Способ решения этих проблем достаточно прост – необходимо убедиться, что файлы, которые запрашивает браузер, присутствуют на сервере. Устранение этой проблемы увеличивает скорость загрузки примерно в 4 раза.

Следует обратить внимание, что обработка ошибки 404 htaccess полностью исключена из настройки системы. Поэтому пользователю необходимо обрабатывать её самостоятельно.

Сообщение о 404 ошибка должно помогать читателю сайта с определением дальнейших действий

Этапы обработки ошибки 404 осуществляются в таком виде:

  • Пользователю поступает ошибка сервера;

  • Идет распознание типа ошибки;

  • Если это ошибка 404, ошибка обрабатывается.

Шаги обработки ошибки 404 htaccess:

  • Установка кода ошибки 404;

  • Создание документа из шаблона;

  • Удаление кода ошибки с сервера. 

Решение проблемы также зависит от сервера, установленного на хостинге.

В том случае, если это сервер Apache, нужно вставить в файл .htaccess следующее:

ErrorDocument 404 /404.html

Рассмотрим код детальнее:

  • 404.html – страница, которая сообщает об ошибке;

  • .htaccess — файл, который содержит правила для сервера Apache.

Если же на хостинге установлен сервер IIS, в таком случае устранять ошибку лучше с помощью менеджера. В настройках сайта выбираем следующее:

HTTP Features->Error Pages

После ищем нужный заголовок и редактируем. В файле Web.config обработка ошибок прописывается так:

<error statusCode="404" prefixLanguageFilePath="" path="/404.php" responseMode="ExecuteURL" />

Если произошла такая ошибка, нужно сообщить об этом пользователю и разъяснить причину. Текст должен содержать информацию о том, что произошел сбой и пользователь может вернуться на главную страницу сайта и воспользоваться картой сайта. Сообщение должно быть информативным и понятным для простого пользователя. Стандартные, краткие ответы необходимо заменить подробными разъяснениями.

Также вы можете наблюдать появление ошибки 404 в файле error_log и прослеживать, какие документы отсутствуют на сервере, чтобы исключить битые ссылки.

После решения проблемы создайте файл 404.html, в котором будет описание того, что делать пользователю при возникшей проблеме. Советы и указания всегда пригодятся вашему пользователю.

Важно, что директива ErrorDocument может обрабатывать не только ошибку 404, но и все остальные ошибки. К примеру, такими способами можно обработать 403 ошибку.

Предложить идею урока:

Опубликовано:
10 апреля 2015

Обновлено:
11 апреля 2019

73 467

Сайты развиваются: создаются новые разделы, меняется структура, удаляются страницы или переделываются их адреса. Часто за всеми этими процессами уследить очень сложно даже опытному веб-мастеру. Чем старше сайт – тем больше вероятность того, что каждый день он получают процент пользователей, попадающих на страницы, которых больше не существует. Как это отследить? Как оповестить робота и клиента, что таких страниц больше нет? Что показывать пользователю на странице 404? Отвечаю!

404-not-found

404 NOT FOUND – что означает?

Определение: 404 ошибка сервера (not found) — самая распространенная ошибка при пользовании Интернетом, основная причина — ошибка в написании адреса Web-страницы. Сервер понял запрос, но не нашёл соответствующего ресурса по указанному URI.”

Для чего нужна 404 страница?

1. Поисковому роботу необходимо сообщить, что такой страницы не существует, для этого используется 404 код ответа сервера. Это очень важно, чтобы не плодились дубли и не размывать релевантность страниц в индексе поисковых систем.

Проверить ответ это очень просто, наберите несуществующий адрес страницы тут – http://bertal.ru/.

not-found-404

2. Пользователю необходимо сообщить, что запрашиваемой страницы больше (или вообще) не существует, и предоставить возможность работать с сайтом дальше.

Как настроить ответ сервера?

404 ошибка сервера через htaccess

Если Ваш сервер или CMS не настроены атоматически, то придётся это сделать Вам самим – добавьте в htaccess строчку:

1
ErrorDocument 404 http://www.site.ru/404.php

Теперь, когда пользователь введёт неверный адрес, то он будет направлен на этот адрес. Страница может располагаться где угодно, но мы для примера поместили ее в корне сайта: /404.php.

404 ошибка сервера в PHP

Велосипеда изобретать не надо – существует специальная функция header, которая успешно поможет Вам это сделать.

1
header(«HTTP/1.0 404 Not Found»);

Как должна выглядеть страница 404?

  • В дизайне сайта (а не страница по умолчанию вашего хостинга)
  • Содержать информацию о том, что произошла ошибка
  • Иметь форму поиска по сайту
  • Иметь небольшую карту сайта с основными разделами.

Креативные 404 страницы – вред или польза?

Смешное оформление 404 страницы – это красиво и оригинально, но не стоит слишком сильно увлекаться. Не стоит забывать, чтобы пользователю в первую очередь необходимо решить какие-то задачи на вашем сайте, а не зависать на 404 странице, Вы должны максимально упростить и помочь ему в достижении его целей.

404

Как отследить, сколько таких пользователей попадают на страницу 404?

Яндекс.Метрика

Для этого удобно использовать “Параметры визитов”.
В код счётчика необходимо добавить строчку: params:window.yaParams||{ }});

Таким образом, должно получиться как-то так:

1
2
3
4
5
6
7
8
w.yaCounterХХХХХХХХ = new Ya.Metrika({id:ХХХХХХХХ,
                    webvisor:true,
                    clickmap:true,
                    trackLinks:true,
                    accurateTrackBounce:true,
                    trackHash:true,
                    ut:"noindex",
                    params:window.yaParams||{ }});

На самой же странице 404 в любом месте необходимо разместить следующий JS-код:

1
2
3
var url = document.location.pathname + document.location.search	
var url_referrer = document.referrer;
var yaParams = {error404: {page: url, from: url_referrer}};

Где url – текущий адрес страницы 404, а url_referrer – адрес, с которого на него попали. Таким образом, мы в Яндекс.Метрике сможем отлеживать не только все 404 страницы, но и адреса, по которым на неё перешли.

Отчёт в Метрике необходимо смотреть тут: все отчеты -> содержание -> параметры визитов.

params

Подробнее о параметрах визита в Яндекс.Метрике: http://help.yandex.ru/metrika/content/visit-params.xml

Google.Analytics

Для отслеживания ошибок используем “события”. Добавляем JS-код в тело страницы:

1
2
3
4
5
6
7
8
9
jQuery(document).ready(function() { 
var url = document.location.pathname + document.location.search	
var url_referrer = document.referrer;
ga('send', {'hitType': 'event', 
	'eventCategory': 'page-404', 
	'eventAction': url, 
	'eventLabel': url_referrer
	});
});

Где hitType – тип события, eventCategory – категория, eventAction – адрес ошибки, url_referrer – откуда на 404 страницу попали.

Отчёт в Гугл.Аналитикс: Поведение -> События -> Обзор.

events

Подробнее о настройке событий в Аналитикс: https://support.google.com/analytics/answer/1033068?hl=ru

Как использовать полученные данные?

Если ошибки 404 внутри сайта – исправьте все ссылки на правильные или уберите вовсе. Если эти ссылки с внешних ресурсов? и Вам никак не повлиять на них, то поставьте 301 редирект на максимально релевантные страницы. Любите своих клиентов и не заставляйте их думать или что-то искать на Вашем сайте.

В статье мы расскажем, что означает ошибка 404 на сайте. Также она может называться:

  • 404 page not found,
  • http error 404,
  • error 404.

Ниже мы опишем, почему возникает ошибка 404, а также как её отследить и исправить.

404 ошибка (http error 404) — что это значит?

Ошибка 404 page not found — это код ответа сервера. Что это значит?

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

Почему такое может произойти? Есть несколько возможных причин:

  1. Вы допустили ошибку при вводе адреса страницы или при открытии файла в браузере. Или браузеру не удалось найти IP-адрес сервера. Сервер не может найти и выдать данные потому, что вы дали ему неправильные «координаты». В таком случае вам достаточно просто исправить ошибку в URL-адресе, и вместо страницы с ошибкой 404 появятся искомые страница/файл.
  2. Данные (страница или файл) не размещены на сервере, или CMS неверно отвечает на запрос пользователя. В этом случае дело обстоит сложнее и быстро справиться с проблемой не получится. Чтобы устранить ошибку 404, определите, как создавался сайт, на котором обнаружена ошибка (на CMS или без использования CMS). От этого будет зависеть способ решения проблемы.

Мы рассмотрим, что делать с ошибкой 404 и как исправить.


Как убрать ошибку 404 на сайте, созданном на CMS (WordPress, Joomla, 1С-Битрикс и т.д.)

На сайтах, созданных с использованием CMS, встречаются различные страницы с ошибкой 404 (http status 404). В зависимости от типа страницы с ошибкой различаются причины возникновения и пути решения проблемы:

  • Если вы видите на своём сайте стандартную ошибку 404 REG.RU:

В большинстве случаев проблема связана с отсутствием конфигурационного файла .htaccess. Как избавиться от ошибки 404? Создайте в корневой папке сайта пустой текстовый файл с расширением .htaccess и добавьте в него стандартные директивы для используемой CMS. Стандартные директивы приведены в статье: Файлы .htaccess для популярных CMS.

Важно: в панели управления cPanel файл .htaccess по умолчанию скрыт (т.е. он существует, но не виден). Следуйте инструкции, чтобы включить отображение файла. Затем сверьте его содержимое со стандартным.

Если файл .htaccess существует и его содержимое корректно, а ошибка 404 not found сохраняется, обратитесь в техническую поддержку.

  • Если вы видите иную страницу ошибки, которую отдает CMS сайта. Например:

Ошибка на WordPress

Пользовательская ошибка 404 not found

Возможно, страница не создана или не опубликована на этапе размещения сайта в админке CMS. Также ошибка может быть связана с формированием «человекопонятных» ЧПУ-ссылок с помощью SEO-плагинов. Чтобы избавиться от проблемы, необходимо обратиться к веб-разработчикам сайта или на тематические форумы, на которых представлена необходимая техническая информация (ошибка http 404).


Как быстро устранить ошибку 404 на сайте, созданном без использования CMS

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

Что означает это сообщение? Запрашиваемые страница/файл отсутствуют или размещены в неправильной папке (не в корневой папке сайта).

Что делать? Откройте корневую папку сайта в панели управления хостингом и проверьте, находятся ли в ней файлы вашего сайта.

  • Если искомые файлы отсутствуют, следуйте инструкции: Как загрузить файл в корневой каталог сайта? После размещения файлов в корневой папке ошибка 404 должна исчезнуть.
  • Если файлы существуют и находятся в корневой папке, обратитесь в техническую поддержку.

Как находить и мониторить код ошибки 404?

Если вы владелец сайта, вы можете найти страницы с ошибкой с помощью специальных сервисов. Самые популярные сервисы:

  • Яндекс.Вебмастер,
  • Google Search Console,
  • Screaming Frog.

Ниже мы описали, как работать с каждым из них.


Яндекс.Вебмастер

  1. Авторизуйтесь в Яндекс.Вебмастер.

  2. Перейдите в раздел Индексирование — Страницы в поиске:

HTTP status 404 — что это

  1. Выберите Исключенные страницы:
  1. Добавьте фильтр, при котором отобразятся результаты с ошибкой «404 page not found»:


Google Search Console

  1. Авторизуйтесь в Google Search Console.

  2. Разверните блок Индекс и выберите Покрытие:

404 ошибка: что это

  1. Используйте фильтр, который покажет код ошибки 404. Для этого перейдите в раздел Сведения и кликните Отправленный URL не найден (ошибка 404):

Ошибка 404: что значит и как исправить


Screaming Frog

  1. Загрузите программу с официального сайта.

  2. Установите её на компьютер.

  3. Откройте программу, введите ссылку на сайт и нажмите Start:

  1. Перейдите во вкладку Response Code. Из выпадающего списка выберите фильтр Client Error (4xx):

Открывается только главная страница сайта, на внутренних страницах ошибка 404 или 500

Рассмотрим, что значит и как исправить ошибку отображения внутренних страниц сайта (error 404 или 500). Причиной проблемы является отсутствие файла .htaccess (либо он пустой, либо в нем не хватает необходимых директив для работы CMS). Решить проблему может замена текущего файла .htaccess стандартным для данной CMS.

На хостинге Linux

Если у вас ISPmanager, проверьте, не включены ли Автоподдомены. Если они включены, отключите их, проверьте актуальность проблемы.

В остальных случаях для устранения внутренней ошибки 404 или 500, перейдите в корневую папку сайта: Как узнать корневую папку сайта
Создайте файл .htaccess (или замените его) со следующим содержимым:


Файл .htaccess для Joomla

##

# @version $Id: htaccess.txt 14401 2010-01-26 14:10:00Z louis $
# @package Joomla
# @copyright Copyright (C) 2005 - 2010 Open Source Matters. All rights reserved.
# @license http://www.gnu.org/copyleft/gpl.html GNU/GPL
# Joomla! is Free Software
##
#####################################################
#  READ THIS COMPLETELY IF YOU CHOOSE TO USE THIS FILE
#
# The line just below this section: 'Options +FollowSymLinks' may cause problems
# with some server configurations.  It is required for use of mod_rewrite, but may already
# be set by your server administrator in a way that dissallows changing it in
# your .htaccess file.  If using it causes your server to error out, comment it out (add # to
# beginning of line), reload your site in your browser and test your sef url's.  If they work,
# it has been set by your server administrator and you do not need it set here.
#
#####################################################

##  Can be commented out if causes errors, see notes above.
Options +FollowSymLinks

#
#  mod_rewrite in use

RewriteEngine On

########## Begin - Rewrite rules to block out some common exploits
## If you experience problems on your site block out the operations listed below
## This attempts to block the most common type of exploit `attempts` to Joomla!
#
## Deny access to extension xml files (uncomment out to activate)
#<Files ~ ".xml$">
#Order allow,deny
#Deny from all
#Satisfy all
#</Files>
## End of deny access to extension xml files
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|%3D) [OR]
# Block out any script trying to base64_encode crap to send via URL
RewriteCond %{QUERY_STRING} base64_encode.*(.*) [OR]
# Block out any script that includes a <script> tag in URL
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
# Send all blocked request to homepage with 403 Forbidden error!
RewriteRule ^(.*)$ index.php [F,L]
#
########## End - Rewrite rules to block out some common exploits

#  Uncomment following line if your webserver's URL
#  is not directly related to physical file paths.
#  Update Your Joomla! Directory (just / for root)

# RewriteBase /


########## Begin - Joomla! core SEF Section
#
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/index.php
RewriteCond %{REQUEST_URI} (/|.php|.html|.htm|.feed|.pdf|.raw|/[^.]*)$  [NC]
RewriteRule (.*) index.php
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
#
########## End - Joomla! core SEF Section


Файл .htaccess для WordPress

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress


Файл .htaccess для HostCMS

Options +FollowSymlinks
AddDefaultCharset Off

<IfModule mod_php4.c>

 php_flag magic_quotes_gpc off

 php_flag magic_quotes_runtime off

 php_flag register_globals off

</IfModule>

<IfModule mod_php5.c>

 php_flag magic_quotes_gpc off

 php_flag magic_quotes_runtime off
 php_flag register_globals off
</IfModule>
<IfModule mod_dir.c>
 DirectoryIndex index.php index.htm index.html
</IfModule>
<IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^(.*)$ /index.php
</IfModule>


Файл .htaccess для Bitrix

Options -Indexes 

ErrorDocument 404 /404.php


<IfModule mod_php5.c>

  php_flag allow_call_time_pass_reference 1

  php_flag session.use_trans_sid off

  #php_value display_errors 1

  #php_value mbstring.func_overload 2
  #php_value mbstring.internal_encoding UTF-8
</IfModule>

<IfModule mod_rewrite.c>
  Options +FollowSymLinks
  RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$
  RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L]
  RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}]
</IfModule>

<IfModule mod_dir.c>
  DirectoryIndex index.php index.html
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive on
  ExpiresByType image/jpeg "access plus 3 day"
  ExpiresByType image/gif "access plus 3 day"
</IfModule>


Файл .htaccess для ModX

# MODx supports Friendly URLs via this .htaccess file. You must serve web

# pages via Apache with mod_rewrite to use this functionality, and you must

# change the file name from ht.access to .htaccess.

#

# Make sure RewriteBase points to the directory where you installed MODx.

# E.g., "/modx" if your installation is in a "modx" subdirectory.

#

# You may choose to make your URLs non-case-sensitive by adding a NC directive

# to your rule: RewriteRule ^(.*)$ index.php?q=$1 [L,QSA,NC]

RewriteEngine On
RewriteBase /

# Rewrite www.domain.com -> domain.com -- used with SEO Strict URLs plugin
#RewriteCond %{HTTP_HOST} .
#RewriteCond %{HTTP_HOST} !^example-domain-please-change.com [NC]
#RewriteRule (.*) http://example-domain-please-change.com/$1 [R=301,L]
#
# or for the opposite domain.com -> www.domain.com use the following
# DO NOT USE BOTH
#
#RewriteCond %{HTTP_HOST} .
#RewriteCond %{HTTP_HOST} !^www.example-domain-please-change.com [NC]
#RewriteRule (.*) http://www.example-domain-please-change.com/$1 [R=301,L]
#
# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent 
# https://www.domain.com when your cert only allows https://secure.domain.com
#RewriteCond %{SERVER_PORT} !^443
#RewriteRule (.*) https://example-domain-please-change.com.com/$1 [R=301,L]
#
# The Friendly URLs part
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
#
# Make sure .htc files are served with the proper MIME type, which is critical # for XP SP2. Un-comment if your host allows htaccess MIME type overrides.
#AddType text/x-component .htc
#
# If your server is not already configured as such, the following directive
# should be uncommented in order to set PHP's register_globals option to OFF.
# This closes a major security hole that is abused by most XSS (cross-site
# scripting) attacks. For more information: http://php.net/register_globals
#
# To verify that this option has been set to OFF, open the Manager and choose
# Reports -> System Info and then click the phpinfo() link. Do a Find on Page
# for "register_globals". The Local Value should be OFF. If the Master Value
# is OFF then you do not need this directive here.
#
# IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS :
#
# Your server does not allow PHP directives to be set via .htaccess. In that
# case you must make this change in your php.ini file instead. If you are
# using a commercial web host, contact the administrators for assistance in
# doing this. Not all servers allow local php.ini files, and they should
# include all PHP configurations (not just this one), or you will effectively
# reset everything to PHP defaults. Consult www.php.net for more detailed
# information about setting PHP directives.
#
#php_flag register_globals Off
#
# For servers that support output compression, you should pick up a bit of
# speed by un-commenting the following lines.
#
#php_flag zlib.output_compression On
#php_value zlib.output_compression_level 5
#
# The following directives stop screen flicker in IE on CSS rollovers. If
# needed, un-comment the following rules. When they're in place, you may have
# to do a force-refresh in order to see changes in your designs.
#
#ExpiresActive On
#ExpiresByType image/gif A2592000
#ExpiresByType image/jpeg A2592000
#ExpiresByType image/png A2592000
#BrowserMatch "MSIE" brokenvary=1
#BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
#BrowserMatch "Opera" !brokenvary
#SetEnvIf brokenvary 1 force-no-vary


Файл .htaccess для Drupal

#

# Apache/PHP/Drupal settings:

#


# Protect files and directories from prying eyes.

<FilesMatch ".(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(.php)?|xtmpl)$|^(..*|Entries.*|Repository|Root|Tag|Template)$">

  Order allow,deny

</FilesMatch>

# Don't show directory listings for URLs which map to a directory.
Options -Indexes

# Follow symbolic links in this directory.
Options +FollowSymLinks

# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php

# Force simple error message for requests for non-existent favicon.ico.
<Files favicon.ico>
  # There is no end quote below, for compatibility with Apache 1.3.
  ErrorDocument 404 "The requested file favicon.ico was not found.
</Files>

# Set the default handler.
DirectoryIndex index.php index.html index.htm

# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_initialize_variables() in
# includes/bootstrap.inc for settings that can be changed at runtime.

# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
  php_flag magic_quotes_gpc                 off
  php_flag magic_quotes_sybase              off
  php_flag register_globals                 off
  php_flag session.auto_start               off
  php_value mbstring.http_input             pass
  php_value mbstring.http_output            pass
  php_flag mbstring.encoding_translation    off
</IfModule>

# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
  # Enable expirations.
  ExpiresActive On

  # Cache all files for 2 weeks after access (A).
  ExpiresDefault A1209600

  <FilesMatch .php$>
    # Do not allow PHP scripts to be cached unless they explicitly send cache
    # headers themselves. Otherwise all scripts would have to overwrite the
    # headers set by mod_expires if they want another caching behavior. This may
    # fail if an error occurs early in the bootstrap process, and it may cause
    # problems if a non-Drupal PHP file is installed in a subdirectory.
    ExpiresActive Off
  </FilesMatch>
</IfModule>

# Various rewrite rules.
<IfModule mod_rewrite.c>
  RewriteEngine on

  # Block access to "hidden" directories whose names begin with a period. This
  # includes directories used by version control systems such as Subversion or
  # Git to store control files. Files whose names begin with a period, as well
  # as the control files used by CVS, are protected by the FilesMatch directive
  # above.
  #
  # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
  # not possible to block access to entire directories from .htaccess, because
  # <DirectoryMatch> is not allowed here.
  #
  # If you do not have mod_rewrite installed, you should remove these
  # directories from your webroot or otherwise protect them from being
  # downloaded.
  RewriteRule "(^|/)." - [F]

  # If your site can be accessed both with and without the 'www.' prefix, you
  # can use one of the following settings to redirect users to your preferred
  # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
  #
  # To redirect all users to access the site WITH the 'www.' prefix,
  # (http://example.com/... will be redirected to http://www.example.com/...)
  # uncomment the following:
  # RewriteCond %{HTTP_HOST} !^www. [NC]
  # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
  #
  # To redirect all users to access the site WITHOUT the 'www.' prefix,
  # (http://www.example.com/... will be redirected to http://example.com/...)
  # uncomment the following:
  # RewriteCond %{HTTP_HOST} ^www.(.+)$ [NC]
  # RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301]

  # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
  # VirtualDocumentRoot and the rewrite rules are not working properly.
  # For example if your site is at http://example.com/drupal uncomment and
  # modify the following line:
  # RewriteBase /drupal
  #
  # If your site is running in a VirtualDocumentRoot at http://example.com/,
  # uncomment the following line:
  # RewriteBase /

  # Pass all requests not referring directly to files in the filesystem to
  # index.php. Clean URLs are handled in drupal_environment_initialize().
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteRule ^ index.php [L]

  # Rules to correctly serve gzip compressed CSS and JS files.
  # Requires both mod_rewrite and mod_headers to be enabled.
  <IfModule mod_headers.c>
    # Serve gzip compressed CSS files if they exist and the client accepts gzip.
    RewriteCond %{HTTP:Accept-encoding} gzip
    RewriteCond %{REQUEST_FILENAME}.gz -s
    RewriteRule ^(.*).css $1.css.gz [QSA]

    # Serve gzip compressed JS files if they exist and the client accepts gzip.
    RewriteCond %{HTTP:Accept-encoding} gzip
    RewriteCond %{REQUEST_FILENAME}.gz -s
    RewriteRule ^(.*).js $1.js.gz [QSA]

    # Serve correct content types, and prevent mod_deflate double gzip.
    RewriteRule .css.gz$ - [T=text/css,E=no-gzip:1]
    RewriteRule .js.gz$ - [T=text/javascript,E=no-gzip:1]

    <FilesMatch "(.js.gz|.css.gz)$">
      # Serve correct encoding type.
      Header append Content-Encoding gzip
      # Force proxies to cache gzipped & non-gzipped css/js files separately.
      Header append Vary Accept-Encoding
    </FilesMatch>
  </IfModule>
</IfModule>


Файл .htaccess для NetCat

AddDefaultCharset windows-1251
ErrorDocument 404 /netcat/require/e404.php
<ifModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ /netcat/require/e404.php?REQUEST_URI=$1 [L,QSA]
</ifModule>


Файл .htaccess для DLE

DirectoryIndex index.php
RewriteEngine On
RewriteBase /
# Редиректы
RewriteRule ^page/(.*)$ index.php?cstart=$1 [L]
# Сам пост
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),([0-9]+),(.*).html(/?)+$ index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&cstart=$5&news_name=$6 [L]
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),(.*).html(/?)+$ index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 [L]
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})/print:page,([0-9]+),(.*).html(/?)+$ engine/print.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 [L]
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*).html(/?)+$ index.php?subaction=showfull&year=$1&month=$2&day=$3&news_name=$4 [L]
RewriteRule ^([^.]+)/page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$ index.php?newsid=$4&news_page=$2&cstart=$3&seourl=$5&seocat=$1 [L]
RewriteRule ^([^.]+)/page,([0-9]+),([0-9]+)-(.*).html(/?)+$ index.php?newsid=$3&news_page=$2&seourl=$4&seocat=$1 [L]
RewriteRule ^([^.]+)/print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$ engine/print.php?news_page=$2&newsid=$3&seourl=$4&seocat=$1 [L]
RewriteRule ^([^.]+)/([0-9]+)-(.*).html(/?)+$ index.php?newsid=$2&seourl=$3&seocat=$1 [L]
RewriteRule ^page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$ index.php?newsid=$3&news_page=$1&cstart=$2&seourl=$4 [L]
RewriteRule ^page,([0-9]+),([0-9]+)-(.*).html(/?)+$ index.php?newsid=$2&news_page=$1&seourl=$3 [L]
RewriteRule ^print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$ engine/print.php?news_page=$1&newsid=$2&seourl=$3 [L]
RewriteRule ^([0-9]+)-(.*).html(/?)+$ index.php?newsid=$1&seourl=$2 [L]

# За день
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})(/?)+$ index.php?year=$1&month=$2&day=$3 [L]
RewriteRule ^([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)(/?)+$ index.php?year=$1&month=$2&day=$3&cstart=$4 [L]
# За весь месяц
RewriteRule ^([0-9]{4})/([0-9]{2})(/?)+$ index.php?year=$1&month=$2 [L]
RewriteRule ^([0-9]{4})/([0-9]{2})/page/([0-9]+)(/?)+$ index.php?year=$1&month=$2&cstart=$3 [L]
# Вывод за весь год
RewriteRule ^([0-9]{4})(/?)+$ index.php?year=$1 [L]
RewriteRule ^([0-9]{4})/page/([0-9]+)(/?)+$ index.php?year=$1&cstart=$2 [L]
# вывод отдельному тегу
RewriteRule ^tags/([^/]*)(/?)+$ index.php?do=tags&tag=$1 [L]
RewriteRule ^tags/([^/]*)/page/([0-9]+)(/?)+$ index.php?do=tags&tag=$1&cstart=$2 [L]
# вывод для отдельного юзера
RewriteRule ^user/([^/]*)/rss.xml$ engine/rss.php?subaction=allnews&user=$1 [L]
RewriteRule ^user/([^/]*)(/?)+$ index.php?subaction=userinfo&user=$1 [L]
RewriteRule ^user/([^/]*)/page/([0-9]+)(/?)+$ index.php?subaction=userinfo&user=$1&cstart=$2 [L]
RewriteRule ^user/([^/]*)/news(/?)+$ index.php?subaction=allnews&user=$1 [L]
RewriteRule ^user/([^/]*)/news/page/([0-9]+)(/?)+$ index.php?subaction=allnews&user=$1&cstart=$2 [L]
RewriteRule ^user/([^/]*)/news/rss.xml(/?)+$ engine/rss.php?subaction=allnews&user=$1 [L]
# вывод всех последних новостей
RewriteRule ^lastnews/(/?)+$ index.php?do=lastnews [L]
RewriteRule ^lastnews/page/([0-9]+)(/?)+$ index.php?do=lastnews&cstart=$1 [L]
# вывод в виде каталога
RewriteRule ^catalog/([^/]*)/rss.xml$ engine/rss.php?catalog=$1 [L]
RewriteRule ^catalog/([^/]*)(/?)+$ index.php?catalog=$1 [L]
RewriteRule ^catalog/([^/]*)/page/([0-9]+)(/?)+$ index.php?catalog=$1&cstart=$2 [L]
# вывод непрочитанных статей
RewriteRule ^newposts(/?)+$ index.php?subaction=newposts [L]
RewriteRule ^newposts/page/([0-9]+)(/?)+$ index.php?subaction=newposts&cstart=$1 [L]
# вывод избранных статей
RewriteRule ^favorites(/?)+$ index.php?do=favorites [L]
RewriteRule ^favorites/page/([0-9]+)(/?)+$ index.php?do=favorites&cstart=$1 [L]
RewriteRule ^rules.html$ index.php?do=rules [L]
RewriteRule ^statistics.html$ index.php?do=stats [L]
RewriteRule ^addnews.html$ index.php?do=addnews [L]
RewriteRule ^rss.xml$ engine/rss.php [L]
RewriteRule ^sitemap.xml$ uploads/sitemap.xml [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.]+)/page/([0-9]+)/$ index.php?do=cat&category=$1&cstart=$2 [L]
RewriteRule ^([^.]+)/$ index.php?do=cat&category=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^.]+)/rss.xml$ engine/rss.php?do=cat&category=$1 [L]
RewriteRule ^page,([0-9]+),([^/]+).html$ index.php?do=static&page=$2&news_page=$1 [L]
RewriteRule ^print:([^/]+).html$ engine/print.php?do=static&page=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+).html$ index.php?do=static&page=$1 [L]


Файл .htaccess для Opencart

Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]


Файл .htaccess для Webasyst

<FilesMatch ".md5$">
Deny from all
</FilesMatch>
DirectoryIndex index.php
Options -Indexes
# Comment the following line, if option Multiviews not allowed here
Options -MultiViews
AddDefaultCharset utf-8
<ifModule mod_rewrite.c>
RewriteEngine On
# Uncomment the following line, if you are having trouble
RewriteBase /
RewriteCond %{REQUEST_URI} !.(js|css|jpg|jpeg|gif|png|svg|ttf|eot|otf|woff|woff2)$ [or]
RewriteCond %{REQUEST_URI} apple-touch-icon.png$ [or]
RewriteCond %{REQUEST_METHOD} ^(POST|PUT|COPY|MOVE|DELETE|PROPFIND|OPTIONS|MKCOL)$ [or]
RewriteCond %{HTTP:Translate} ^.+$ [or]
RewriteCond %{HTTP_USER_AGENT} ^(DavClnt|litmus|gvfs|davfs|wdfs|WebDAV|cadaver|Cyberduck)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]
</ifModule>
<ifModule mod_headers.c>
<FilesMatch ".(jpg|jpeg|png|gif|js|css|svg|ttf|eot|otf|woff|woff2)$">
Header set Cache-Control "max-age=3153600, public"
</FilesMatch>
</ifModule>

Если у вас хостинг Windows

На хостинге Windows файл .htaccess не поддерживается. Его функцию выполняет файл web.config. Если вы наблюдаете внутреннюю ошибку 404 или 500 на хостинге Windows, рекомендуем обратиться к разработчикам сайта или на тематические форумы с вопросом, как убрать 404, заменив файл web.config.

Что будет, если не исправлять ошибку 404

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

Во-вторых, есть риск потерять хорошую позицию в поисковой выдаче. Сама по себе страница с ошибкой 404 не вызывает у поисковой системы недоверия. Она просто удаляется из индексации. Однако там могли находиться ключевые слова, которые могли повлиять положительно на поисковую выдачу. Если на сайте много страниц с ошибкой, тогда поисковые роботы действительно могут отнестись с недоверием ко всему веб-ресурсу и сайт может потерять высокий рейтинг.

Сделайте страницу 404 полезной

Ошибка 404 (страница не найдена) может появиться в любое время. Важно, чтобы пользователь при входе на эту страницу не потерял доверия к сайту. Страницы с ошибкой 404 можно создавать самостоятельно. Например, если у вас сайт на WordPress или вы пользуетесь услугой REG.Site, страницу с ошибкой можно легко создать с помощью плагина 404page.

Вот несколько советов по созданию страницы:

  • дизайн этой страницы должен соответствовать всему ресурсу (цвет, шрифт, иллюстрации),
  • поместите ссылку на главную страницу,
  • добавьте дайджесты последних публикаций на сайте,
  • поместите контакты организации (номер телефона, адрес) и службы поддержки,
  • можно предложить действия для решения проблемы доступа к странице.

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

Если перечисленные способы не помогли исправить ошибку, обратитесь в службу поддержки REG.RU.

Видеосправка. Об ошибке 404 и как создать страницу для неё

При разработке проекта на ASP.NET MVC возникла необходимость сделать собственную страницу ошибки 404. Я рассчитывал, что справлюсь с этой задачей за несколько минут. Через 6 часов работы я определил два варианта ее решения разной степени сложности. Описание — далее.

В ASP.NET MVC 3, с которой я работаю, появились глобальные фильтры. В шаблоне нового проекта, уже встроено ее использование для отображения собственной страницы ошибок (через HandleErrorAttribute). Замечательный метод, только он не умеет обрабатывать ошибки с кодом 404 (страница не найдена). Поэтому пришлось искать другой красивый вариант обработки этой ошибки.

Обработка ошибки 404 через Web.config

Платформа ASP.NET предоставляет возможность произвольно обрабатывать ошибки путем нехитрой настройки файла Web.config. Для это в секцию system.web нужно добавить следующий код:

<customErrors mode="On" >
       <error statusCode="404" redirect="/Errors/Error404/" />
</customErrors>

При появлении ошибки 404 пользователь будет отправлен на страницу yoursite.ru/Errors/Error404/?aspxerrorpath=/Not/Found/Url. Кроме того, что это не очень красиво и удобно (нельзя отредактировать url), так еще и плохо для SEO — статья на habr.ru.
Способ можно несколько улучшить, добавив redirectMode=«ResponseRewrite» в customErrors:

<customErrors mode="On" redirectMode="ResponseRewrite" >
       <error statusCode="404" redirect="/Errors/Error404/" />
</customErrors>

В данном случае должен происходить не редирект на страницу обработки ошибки, а подмена запрошенного ошибочного пути содержимым указанной страницы. Однако, и здесь есть свои сложности. На ASP.NET MVC этот способ в приведенном виде не работает. Достаточно подробное обсуждение (на английском) можно прочитать в топике. Кратко говоря, этот метод основан на методе Server.Transfer, который используется в классическом ASP.NET и, соответственно, работает только со статическими файлами. С динамическими страницами, как в примере, он работать отказывается (так как не видит на диске файл  ‘/Errors/Error404/’). То есть, если заменить ‘/Errors/Error404/’ на, например, ‘/Errors/Error404.htm’, то описанные метод будет работать. Однако в этом случае не получится выполнять дополнительные действия по обработке ошибок, например, логирование.
В указанном топике было предложено добавить в каждую страницу следующий код:

Response.TrySkipIisCustomErrors = true;

Этот способ работает только с IIS 7 и выше, поэтому проверить этот метод не удалось — используем IIS 6. Поиски пришлось продолжить.

Танцы с бубном и Application_Error

Если описанный выше метод применить по каким-либо причинам не удается, то придется писать больше строк кода. Частичное решение приведено в статье.
Наиболее полное решение «с бубном» я нашел в топике. Обсуждение ведется на английском, поэтому переведу текст решения на русский.
Ниже приведены мои требования к решению проблемы отображения ошибки 404 NotFound:

  • Я хочу обрабатывать пути, для которых не определено действие.
  • Я хочу обрабатывать пути, для которых не определен контроллер.
  • Я хочу обрабатывать пути, которые не удалось разобрать моему приложению. Я не хочу, чтобы эти ошибки обрабатывались в Global.asax или IIS, потому что потом я не смогу сделать редирект обратно на мое приложение.
  • Я хочу обрабатывать собственные (например, когда требуемый товар не найден по ID) ошибки 404 в едином стиле.
  • Я хочу, чтобы все ошибки 404 возвращали MVC View, а не статическую страницу, чтобы потом иметь возможность получить больше данных об ошибках. И они должны возвращать код статуса 404.

Я думаю, что Application_Error в Global.asax должен быть использован для целей более высокого уровня, например, для обработки необработанных исключений или логирования, а не работы с ошибкой 404. Поэтому я стараюсь вынести весь код, связанный с ошибкой 404, вне файла Global.asax.

Шаг 1: Создаем общее место для обработки ошибки 404

Это облегчит поддержку решение. Используем ErrorController, чтобы можно было легче улучшать страницу 404 в дальнейшем. Также нужно убедиться, что контроллер возвращает код 404!

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // Если путь относительный ('NotFound' route), тогда его нужно заменить на запрошенный путь
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Предотвращаем зацикливание при равенстве Referrer и Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: добавить реализацию ILogger

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}
Шаг 2: Используем собственный базовый класс для контроллеров, чтобы легче вызывать метод для ошибки 404 и обрабатывать HandleUnknownAction

Ошибка 404 в ASP.NET MVC должна быть обработана в нескольких местах. Первое — это HandleUnknownAction.
Метод InvokeHttp404 является единым местом для перенаправления к ErrorController и нашему вновь созданному действию Http404. Используйте методологию DRY!

public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // Если контроллер - ErrorController, то не нужно снова вызывать исключение
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}
Шаг 3: Используем инъекцию зависимостей в фабрике контроллеров и обрабатываем 404 HttpException

Например, так (не обязательно использовать StructureMap):
Пример для MVC1.0:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

Пример для MVC2.0:

 protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    try
    {
        if (controllerType == null)
            return base.GetControllerInstance(requestContext, controllerType);
    }
    catch (HttpException ex)
    {
        if (ex.GetHttpCode() == 404)
        {
            IController errorController = ObjectFactory.GetInstance<ErrorController>();
            ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

            return errorController;
        }
        else
            throw ex;
    }

    return ObjectFactory.GetInstance(controllerType) as Controller;
}

Я думаю, что лучше отлавливать ошибки в месте их возникновения. Поэтому я предпочитаю метод, описанный выше, обработке ошибок в Application_Error.
Это второе место для отлова ошибок 404.

Шаг 4: Добавляем маршрут NotFound в Global.asax для путей, которые не удалось определить нашему приложению

Этот маршрут должен вызывать действие Http404. Обратите внимание, что параметр url будет относительным адресом, потому что движок маршрутизации отсекает часть с доменным именем. Именно поэтому мы добавили все эти условные операторы на первом шаге.

        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

Это третье и последнее место в приложении MVC для отлова ошибок 404, которые Вы не вызываете самостоятельно. Если здесь не удалось сопоставить входящий путь ни какому контроллеру и действию, то MVC передаст обработку этой ошибки дальше платформе ASP.NET (в файл Global.asax). А мы не хотим, чтобы это случилось.

Шаг 5: Наконец, вызываем ошибку 404, когда приложению не удается что-либо найти

Например, когда нашему контроллеру Loan, унаследованному от MyController, передан неправильный параметр ID:

//
// GET: /Detail/ID

public ActionResult Detail(int ID)
{
    Loan loan = this._svc.GetLoans().WithID(ID);
    if (loan == null)
        return this.InvokeHttp404(HttpContext);
    else
        return View(loan);
}

Было бы замечательно, если бы можно было все это реализовать меньшим количеством кода. Но я считаю, что это решение легче поддерживать, тестировать, и в целом оно более удобно.

Библиотека для второго решения

Ну и на последок: уже готова библиотека, позволяющая организовать обработку ошибок описанным выше способом. Найти ее можно здесь — github.com/andrewdavey/NotFoundMvc.

Заключение

Интереса ради я посмотрел, как эта задача решена в Orchard. Был удивлен и несколько разочарован — разработчики решили вообще не обрабатывать это исключение — собственные страницы ошибок 404, на мой взгляд, давно стали стандартом в веб-разработке.

В своем приложении я использовал обработку ошибки через Web.config с использованием роутинга. До окончания разработки приложения, а останавливаться на обработке ошибки 404 довольно опасно — можно вообще приложение тогда никогда не выпустить. Ближе к окончанию, скорее всего, внедрю второе решение.

Ссылки по теме:

  1. ASP.NET, HTTP 404 и SEO.
  2. CustomErrors does not work when setting redirectMode=«ResponseRewrite».
  3. How can I properly handle 404 in ASP.NET MVC?
  4. Обработка ошибок для всего сайта в ASP.NET MVC 3.
  5. ASP.NET MVC 404 Error Handling.
  6. HandleUnknownAction in ASP.NET MVC – Be Careful.
  7. github.com/andrewdavey/NotFoundMvc.

Хотя PHP уже давно поддерживает обработку исключений, однако, по сравнению с Java эта поддержка была довольно слабой

Первоначальная поддержка обработки исключений была введена в язык с 5 версии PHP, с двумя простыми встроенными классами исключений — Exception и ErrorException, с поддержкой дополнительных классов через SPL. Идея этого поста состоит в том, чтобы представить читателям современные возможности обработки исключений PHP. 

Новый интерфейс

Хотя PHP 7 предоставляет классы Error и Exception, давайте сначала затронем интерфейс Throwable . И Error и Exception классы реализуют Throwable интерфейс — это основа для любого объекта , который может быть брошен с помощью оператора throw. Единственное, что он не может быть реализован непосредственно в классах пользовательского пространства, только через расширение класса Exception. Кроме того, он обеспечивает единую точку для отлова обоих типов ошибок в одном выражении:

<?php

try {
// ваш код
} catch (Throwable $e) {
echo 'Очень хороший способ отловить исключения и ошибки';
}

Список доступных встроенных классов исключений начиная с PHP 7.4:

  • Exception
  • ErrorException
  • Error
  • ArgumentCountError
  • ArithmeticError
  • AssertionError
  • DivisionByZeroError
  • CompileError
  • ParseError
  • TypeError

Дополнительные классы исключений можно найти в стандартной библиотеке PHP . И наиболее заметным из расширений JSON является класс JsonException.

THROWABLE

Интерфейс Throwable  PHP 7:

interface Throwable
{
public function getMessage(): string; // Error reason
public function getCode(): int; // Error code
public function getFile(): string; // Error begin file
public function getLine(): int; // Error begin line
public function getTrace(): array; // Return stack trace as array like debug_backtrace()
public function getTraceAsString(): string; // Return stack trace as string
public function getPrevious(): Throwable; // Return previous `Trowable`
public function __toString(): string; // Convert into string
}

Вот иерархия Throwable:

interface Throwable
|- Error implements Throwable
|- ArithmeticError extends Error
|- DivisionByZeroError extends ArithmeticError
|- AssertionError extends Error
|- ParseError extends Error
|- TypeError extends Error
|- ArgumentCountError extends TypeError
|- Exception implements Throwable
|- ClosedGeneratorException extends Exception
|- DOMException extends Exception
|- ErrorException extends Exception
|- IntlException extends Exception
|- LogicException extends Exception
|- BadFunctionCallException extends LogicException
|- BadMethodCallException extends BadFunctionCallException
|- DomainException extends LogicException
|- InvalidArgumentException extends LogicException
|- LengthException extends LogicException
|- OutOfRangeException extends LogicException
|- PharException extends Exception
|- ReflectionException extends Exception
|- RuntimeException extends Exception
|- OutOfBoundsException extends RuntimeException
|- OverflowException extends RuntimeException
|- PDOException extends RuntimeException
|- RangeException extends RuntimeException
|- UnderflowException extends RuntimeException
|- UnexpectedValueException extends RuntimeException

Ошибка, почему?

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

Проблема заключалась в том, что было несколько фатальных ошибок, которые не могли быть обработаны определяемым пользователем обработчиком ошибок. Это означало, что вы не могли корректно обрабатывать фатальные ошибки в PHP. Было несколько побочных эффектов, которые были проблематичными, такие как потеря контекста времени выполнения, деструкторы не вызывались, да и вообще иметь дело с ними было неудобно. В PHP 7 фатальные ошибки теперь являются исключениями, и мы можем легко их обработать. Фатальные ошибки приводят к возникновению исключений. Вам необходимо обрабатывать нефатальные ошибки с помощью функции обработки ошибок.

Вот пример ловли фатальной ошибки в PHP 7.1. Обратите внимание, что нефатальная ошибка не обнаружена.

<?php 

try {
// будет генерировать уведомление, которое не будет поймано
echo $someNotSetVariable;
// фатальная ошибка, которая сейчас на самом деле ловится
someNoneExistentFunction();
} catch (Error $e) {
echo "Error caught: " . $e->getMessage();
}

Этот скрипт выведет сообщение об ошибке при попытке доступа к недопустимой переменной. Попытка вызвать функцию, которая не существует, приведет к фатальной ошибке в более ранних версиях PHP, но в PHP 7.1 вы можете ее перехватить. Вот вывод для скрипта:

Notice: Undefined variable: someNotSetVariable on line 3
Error caught: Call to undefined function someNoneExistentFunction()

Константы ошибок

В PHP много констант, которые используются в отношении ошибок. Эти константы используются при настройке PHP для скрытия или отображения ошибок определенных классов.

Вот некоторые из наиболее часто встречающихся кодов ошибок:

  • E_DEPRECATED — интерпретатор сгенерирует этот тип предупреждений, если вы используете устаревшую языковую функцию. Сценарий обязательно продолжит работать без ошибок.
  • E_STRICT — аналогично E_DEPRECATED, — указывает на то, что вы используете языковую функцию, которая не является стандартной в настоящее время и может не работать в будущем. Сценарий будет продолжать работать без каких-либо ошибок.
  • E_PARSE — ваш синтаксис не может быть проанализирован, поэтому ваш скрипт не запустится. Выполнение скрипта даже не запустится.
  • E_NOTICE — движок просто выведет информационное сообщение. Выполнение скрипта не прервется, и ни одна из ошибок не будет выдана.
  • E_ERROR — скрипт не может продолжить работу, и завершится. Выдает ошибки, а как они будут обрабатываться, зависит от обработчика ошибок.
  • E_RECOVERABLE_ERROR — указывает на то, что, возможно, произошла опасная ошибка, и движок работает в нестабильном состоянии. Дальнейшее выполнение зависит от обработчика ошибок, и ошибка обязательно будет выдана.

Полный список констант можно найти в руководстве по PHP.

Функция обработчика ошибок

Функция set_error_handler() используется, чтобы сообщить PHP как обрабатывать стандартные ошибки, которые не являются экземплярами класса исключений Error. Вы не можете использовать функцию обработчика ошибок для фатальных ошибок. Исключения ошибок должны обрабатываться с помощью операторов try/catch. set_error_handler() принимает callback функцию в качестве своего параметра. Callback-функции в PHP могут быть заданы двумя способами: либо строкой, обозначающей имя функции, либо передачей массива, который содержит объект и имя метода (именно в этом порядке). Вы можете указать защищенные и приватные методы для callable в объекте. Вы также можете передать значение null, чтобы указать PHP вернуться к использованию стандартного механизма обработки ошибок. Если ваш обработчик ошибок не завершает программу и возвращает результат, ваш сценарий будет продолжать выполняться со строки, следующей за той, где произошла ошибка.

PHP передает параметры в вашу функцию обработчика ошибок. Вы можете опционально объявить их в сигнатуре функции, если хотите использовать их в своей функции.

Вот пример:

<?php

function myCustomErrorHandler(int $errNo, string $errMsg, string $file, int $line) {
echo "Ух ты, мой обработчик ошибок получил #[$errNo] в [$file] на [$line]: [$errMsg]";
}

set_error_handler('myCustomErrorHandler');

try {
why;
} catch (Throwable $e) {
echo 'И моя ошибка: ' . $e->getMessage();
}

Если вы запустите этот код в PHP-консоли php -a, вы должны получить похожий вывод:

Error #[2] occurred in [php shell code] at line [3]: [Use of undefined constant why - assumed 'why' (this will throw an Error in a future version of PHP)]

Самые известные PHP библиотеки , которые делают обширное использование РНР set_error_handler() и могут сделать хорошие представления исключений и ошибок являются whoops,  и Symony Debug, ErrorHandler компоненты. Я смело рекомендую использовать один из них. Если не собираетесь их использовать в своем проекте, то вы всегда можете черпать вдохновение из их кода. В то время как компонент Debug широко используется в экосистеме Symfony, Whoops остается библиотекой выбора для фреймворка Laravel . 

Для подробного и расширенного использования, пожалуйста, обратитесь к руководству по PHP для обработчика ошибок.

Отображение или подавление нефатальной ошибки

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

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

Для этого вам нужно настроить PHP, используя следующие параметры в вашем файле php.ini:

  • display_errors – может быть установлен в false для подавления сообщений
  • log_errors – может использоваться для хранения сообщений об ошибках в файлах журнала
  • error_reporting – можно настроить, какие ошибки вызывают отчет

Лучше всего корректно обрабатывать ошибки в вашем приложении. В производственном процессе вы должны скорее регистрировать необработанные ошибки, чем разрешать их отображение пользователю. Функция error_log() может использоваться для отправки сообщения одной из определенных процедур обработки ошибок. Вы также можете использовать функцию error_log() для отправки электронных писем, но лично вы бы предпочли использовать хорошее решение для регистрации ошибок и получения уведомлений при возникновении ошибок, например Sentry или Rollbar .

Существует вещь, называемая оператором контроля ошибок ( @ ), который по своей сути может игнорировать и подавлять ошибки. Использование очень простое — просто добавьте любое выражение PHP с символом «собаки», и сгенерированная ошибка будет проигнорирована. Хотя использование этого оператора может показаться интересным, я призываю вас не делать этого. Мне нравится называть это живым пережитком прошлого.

Больше информации для всех функций, связанных с ошибками PHP, можно найти в руководстве.

Исключения (Exceptions)

Исключения являются основной частью объектно-ориентированного программирования и впервые были представлены в PHP 5.0. Исключением является состояние программы, которое требует специальной обработки, поскольку оно не выполняется ожидаемым образом. Вы можете использовать исключение, чтобы изменить поток вашей программы, например, чтобы прекратить что-либо делать, если некоторые предварительные условия не выполняются.

Исключение будет возникать в стеке вызовов, если вы его не перехватите. Давайте посмотрим на простой пример:

try {
print "это наш блок попыток n";
throw new Exception();
} catch (Exception $e) {
print "что-то пошло не так, есть улов!";
} finally {
print "эта часть всегда выполняется";
}

PHP включает в себя несколько стандартных типов исключений, а стандартная библиотека PHP (SPL) включает в себя еще несколько. Хотя вам не нужно использовать эти исключения, это означает, что вы можете использовать более детальное обнаружение ошибок и отчеты. Классы Exception и Error реализуют интерфейс Throwable и, как и любые другие классы, могут быть расширены. Это позволяет вам создавать гибкие иерархии ошибок и адаптировать обработку исключений. Только класс, который реализует класс Throwable, может использоваться с ключевым словом throw. Другими словами, вы не можете объявить свой собственный базовый класс и затем выбросить его как исключение.

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

Ловля исключений

Вы должны использовать try/catch структуру:

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так.');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
}

Как видите, есть два предложения catch. Исключения будут сопоставляться с предложениями сверху вниз, пока тип исключения не будет соответствовать предложению catch. Эта очень простая функция throwMyCustomException() генерирует исключение MyCustomException, и мы ожидаем, что оно будет перехвачено в первом блоке. Любые другие исключения, которые произойдут, будут перехвачены вторым блоком. Здесь мы вызываем метод getMessage() из базового класса Exception. Вы можете найти больше информации о дополнительном методе в Exception PHP docs.

Кроме того, можно указать несколько исключений, разделяя их трубой ( | ).

Давайте посмотрим на другой пример:

<?php

class MyCustomException extends Exception { }
class MyAnotherCustomException extends Exception { }

try {
throw new MyAnotherCustomException;
} catch (MyCustomException | MyAnotherCustomException $e) {
echo "Caught : " . get_class($e);
}

Этот очень простой блок catch будет перехватывать исключения типа MyCustomException и MyAnotherCustomException.

Немного более продвинутый сценарий:

// exceptions.php
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;

try {
throw new NotFoundHttpException();
} catch (Exception $e) {
echo 1;
} catch (NotFoundHttpException $e) {
echo 2;
} catch (Exception $e) {
echo 3;
} finally {
echo 4;
}

Это ваш окончательный ответ?

В PHP 5.5 и более поздних, блок finally также может быть указан после или вместо блоков catch. Код внутри блока finally всегда будет выполняться после блоков try и catch независимо от того, было ли выброшено исключение, и до возобновления нормального выполнения. Одним из распространенных применений блока finally является закрытие соединения с базой данных, но, наконец, его можно использовать везде, где вы хотите, чтобы код всегда выполнялся.

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
} finally {
echo "Я всегда тут";
}

Вот хороший пример того, как работают операторы PHP catch/finally:

<?php

try {
try {
echo 'a-';
throw new exception();
echo 'b-';
} catch (Exception $e) {
echo 'пойманный-';
throw $e;
} finally {
echo 'завершенный-';
}
} catch (Exception $e) {
echo 'конец-';
}

Функция обработчика исключений

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

Для этого вы используете функцию set_exception_handler() , которая принимает вызываемый элемент в качестве параметра. Ваш сценарий завершится после того, как вызов будет выполнен.

Функция restore_exception_handler() вернет обработчик исключений к его предыдущему значению.

<?php

class MyCustomException extends Exception { }

function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "n";
}

set_exception_handler('exception_handler');

try {
throw new Exception('Uncaught Exception');
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} finally {
echo "Я всегда тут";
}

print "Не выполнено";

Здесь простая функция exception_handler будет выполняться после блока finally, когда ни один из типов исключений не был сопоставлен. Последний вывод никогда не будет выполнен.

Для получения дополнительной информации обратитесь к документации PHP. 

Старый добрый «T_PAAMAYIM_NEKUDOTAYIM»

Вероятно, это было самое известное сообщение об ошибке PHP. В последние годы было много споров по этому поводу. Вы можете прочитать больше в отличном сообщении в блоге от Фила Осетра .  

Сегодня я с гордостью могу сказать, что если вы запустите этот код с PHP 7, то сообщение о T_PAAMAYIM_NEKUDOTAYIM больше не будет:

<?php

class foo
{
static $bar = 'baz';
}

var_dump('foo'::$bar);

// Output PHP < 7.0:
// PHP Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM) in php shell code on line 1
// Output PHP > 7.0:
// string(3) "baz"
?>

В заключении

Со времени первого внедрения обработки исключений в PHP прошло много лет, пока мы не получили гораздо более надежную и зрелую обработку исключений, чем в Java. Обработка ошибок в PHP 7 получила много внимания, что делает его хорошим, открывая пространство для будущих улучшений, если мы действительно с сегодняшней точки зрения нуждаемся в них.

Понравилась статья? Поделить с друзьями:
  • Как обрабатывать ошибки запросов js
  • Как обойти ошибку на vba
  • Как обрабатывать ошибки в питоне
  • Как обойти ошибку доступа в вк
  • Как обрабатывать ошибки в react