Category: howto

[Grails] favicon.ico и robots.txt

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

Где должна быть иконка для закладки?

Например favicon.ico — очень важная маленькая вещь. Иконка значительно увеличивает узнаваемость закладки.
По умолчанию в main layout который генерирует Grails есть прописанный путь к favicon:

<html>
<head>
    ...
    <link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon">
...
</head>

Броузер должен понять что фавикон лежит по URL /static/images/favicon.ico

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

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

Как решать?

Первое что приходит на ум это прописать в UrlMappings перенаправление на правильный путь к иконке. Но конечно же не стоит этого делать, ведь мы помним что всё что лежит в папке web-app становится доступным по прямо ссылке.
Так что нам достаточно просто перенести фавикон в директорию уровнем выше:

mv grails-app/web-app/images/favicon.ico grails-app/web-app/favicon.ico

И ещё подправить наш лейаут grails-app/views/layouts/main.gsp

<html>
<head>
    ...
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
...
</head>

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

Защищаемся от поисковика

Наверное многие помнят историю когда все SMS отправленные с сайта Билайна стали доступны через поисковик яндекса. Это произошло по стечению обстоятельств, одним из которых было то что разработчики не запретили поисковикам индексировать страницы отправки СМС.
Чтобы себя уберечь вам следует тоже запретить поисковикам индексировтаь админку вашего сайта или другие приватные разделы.
Для этого в корень вашего сайта вы должны разметсить файл robots.txt с примерно таким содержимым:

User-agent: *
Disallow: /
Allow: /faq/

Поисковики когда будут индексировать ваш сайт будут соблюдать эти правила и не будут индексировать весь ваш сайт кроме страницы /faq/

Как не сложно догадаться файл robots.txt тоже нужно положить в web-app чтобы он стал доступен по прямой ссылке.

Если вам уже не достаточно простых настроек robots.txt то вам стоит обратить внимание на его XML развитие Sitemaps.
Для Grails даже есть плагин grails-sitemapper, но он выглядит заброшенным так что десять раз перепроверьте его содержимое.

По хорошему эти две вещи нужно сделать частью стандартного Grails. Как нибудь создам тикет в трекере на эту тему.

Реклама

[Grails] i18n: LocaleResolver, Accept-Language

Warning!!! I made plugin grails-locale-configuration-plugin that replace this solution. [Grails] Сегодня опубликовал стабильную версию grails-locale-configuration-plugin
Also this chapter of book may be interest for you

В каждом броузере пользователь может настроить предпочтительные языки. Например в хроме Settings / Languages (chrome://settings/languages) они выглядят так:
chrome languages settings
Тут указано что пользователь хочет видеть американский английский, или любой английский, но если его не будет тогда на русском, а если и на русском не будет, тогда давайте уже на украинском.

Эти параметры передаются в каждом запросе на сервер через заголовок Accept-Language:

Accept-Language:en-US,en;q=0.8,ru;q=0.6,uk;q=0.4

С помощью параметра q (quality value) мы передаём приоритет от 0 до 1.

Grails умеет работать с этим заголовком и автоматически переключать локаль. Если есть интернационализация для этого языка то она сразу же автоматически показывается и всё хорошо. А вот если не находится i18n/messages_xx.properties для нужной локали, тогда отображается текст по умолчанию из i18n/messages.properties, обычно английский.

Хоть текст и будет английский, но системная локаль будет установлена в ту которую пользователь запросил больше всего.
Например, у нас есть пользователь у которого украинский язык на первом месте и русский на втором.
Запросив страницу Grails запомнит украинскую локаль в сессии, но отобразит всё на английском.
Это не всегда хорошо, особенно если ваш сайт жёстко поддерживает только несколько локалей. Например в зависимости от локали вы разные флажки отображаете.
Скорее всего что захотите ограничить локали которые поддерживаете.
Для этого создайте в файле Config.groovy опцию supportedLocales:

supportedLocales = [Locale.ENGLISH, new Locale('RU')]

А теперь создадим фильтр который будет проверять локаль

class LocaleResolverFilterFilters {

    def filters = {
        all(controller: '*', action: '*') {
            before = {
                // Сначала ищем такую же локаль, если не нашли то локаль с тем же языком, если не нашли то по умолчанию английский
                Locale selectedLocale
                LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request)
                List&amp;amp;amp;lt;Locale&amp;amp;amp;gt; supportedLocales = grailsApplication.config.supportedLocales
                if (request.locale in supportedLocales) {
                    selectedLocale = request.locale
                } else {
                    selectedLocale = findLocaleWithSameLanguage(request, supportedLocales)
                }
                selectedLocale = selectedLocale ?: Locale.ENGLISH
                localeResolver.setLocale(request, response, selectedLocale)
            }
        }
    }

    private Locale findLocaleWithSameLanguage(HttpServletRequest request, List&amp;amp;amp;lt;Locale&amp;amp;amp;gt; supportedLocales) {
        supportedLocales.find({ it.language == request.locale.language })
    }
}

В дальнейшем из кода получить локаль мы можем через объект запроса request.locale а список всех локалей предпочитаемых пользователем через request.locales.
Ту локаль с которой мы отрисовали страницу можно увидеть через объект ответа: response.locale.
Смотрите демо приложение на гитхабе.

Grails request.locale

Что ещё почитать по теме: