• Как узнать размер тела ответа в Java Servlet Filter?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Одно из возможных решений заключается в следующем.
    - подменяем в цепочке фильтров (filterChain.doFilter(...)) объект response на кастомный, который умеет фиксировать контент и позволяет получать содержимое буфера;
    - после filterChain.doFilter(...) с подменённым респонсом используем реализованные для него методы и получаем контент, а уже из контента легко считаем contentLength;

    Код.
    ContentCaptureResponse.class

    public class ContentCaptureResponse extends HttpServletResponseWrapper {
    
        private ByteArrayOutputStream contentBuffer;
        private PrintWriter writer;
    
        public ContentCaptureResponse(HttpServletResponse response) {
            super(response);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            if (writer == null) {
                writer = new PrintWriter(getContentBuffer());
            }
    
            return writer;
        }
    
        private ByteArrayOutputStream getContentBuffer() {
            if (contentBuffer == null) {
                contentBuffer = new ByteArrayOutputStream();
            }
    
            return contentBuffer;
        }
    
        public String getContent() throws IOException {
            getWriter().flush();
    
            return new String(getContentBuffer().toString());
        }
    }



    Фильтр, RequestTrack.class

    @WebFilter(urlPatterns={"/*"})
    public class RequestTrack implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {}
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            ContentCaptureResponse contentCaptureResponse = new ContentCaptureResponse(httpResponse);
    
            filterChain.doFilter(servletRequest, contentCaptureResponse);
            String content = contentCaptureResponse.getContent();
            // content.length();
            // оригинальный response у нас будет пуст, так как в doFilter() мы подменили объект, поэтому пишем в outputStream оригинального response контент, который получили в ответе
            servletResponse.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8));
        }
    
        @Override
        public void destroy() {}
    }

    Ответ написан
    Комментировать
  • Как настроить коммуникацию между двумя Docker Compose проектами на одной машине по HTTP?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Реализовать задуманное можно с помощью прокси.
    В самом примитивном варианте это будет что-то вроде nginx-proxy.
    Можно так же использовать Traefik, но с ним не задалось, так как почему-то всегда один из хостов поставлялся с авто-балансировкой в зависимости от того, какой компоуз был поднят первым (если api, то api.local отвечал всегда с нужного сервиса, а front.local постоянно переключался то на api, то на front сервис, балансируя между ними), проблему не смог решить и пошёл более простым способом -- воспользовался nginx-proxy.
    Если кто знает как правильно сконфигурировать Traefik, чтобы не возникло этой проблемы с балансировкой -- отпишите в комментарии.

    Nginx Proxy запускается так же в Докере, как контейнер, можно через docker-compose:
    version: '3'
    
    services:
      nginx-proxy:
        image: jwilder/nginx-proxy
        container_name: nginx-proxy
        ports:
          - "80:80"
        networks:
          - proxy
        volumes:
          - /var/run/docker.sock:/tmp/docker.sock:ro
    
    networks:
      proxy:
        driver: bridge
    # вне этого docker-compose.yml сеть будет доступна по имени {директория}_proxy, в моём случае это nginx_proxy


    Конфигурация Docker Compose для хостов (описано только самое важное):
    # ...
    services:
      # Web server
      web:
        image: nginx
        environment:
          - 'VIRTUAL_HOST=${NGINX_HOST}' # подтягиваем из конфигурации окружения (.env, NGINX_HOST=api.local)
        networks:
           nginx_proxy: # чтобы наш хостовый nginx мог достучаться по общей с прокси сети до других контейнеров в той же сети
               aliases:
                   - ${NGINX_HOST} # указываем алиас, по которому будет доступен контейнер, в моём случае это локальный хост api.local
           default: # дефолтная сеть, чтобы не ломать связь внутри текущей Docker Compose конфигурации
    
      # PHP
      php:
        image: php
        networks:
          - nginx_proxy # возможность из PHP иметь доступ к общей прокси сети, чтобы взаимодействовать с другими хостами
          - default
    
    networks:
      nginx_proxy: # возьмём сеть извне
        external: true

    Примерно повторяем конфигурирование для front-сервиса.
    Сначала стартуем прокси, потом уже хосты.
    Теперь хост front.local может обратиться к api.local по имени хоста (например, curl или file_get_contents('http://api.local/')).
    Ответ написан
    Комментировать
  • Как правильно инициализировать Vue-компонент после появления элемента в DOM?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Проблема решилась подключенем JS файла прямо в HTML, который прилетает вместе с контентом модального окна.
    Видимо, сразу так и нужно было сделать, хотя и выглядит грубовато.
    Ответ написан
    Комментировать
  • Почему Yii2 при POST AJAX возвращает 500 код но при том в ответе содержится корректный набор данных?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    В логах ничего не было, эмпирическим путём был выявлена проблема: E_WARNING, который, почему-то, нигде не отображался, пока не перехватил вручную, повесив обработчик ошибок
    Решение (UPD)
    set_error_handler(
                function ($errno, $errstr) use($data) {
                    var_dump($errstr);
                },
                E_ALL
            );

    session_write_close(): open(/var/www/mysite/data/mod-tmp/sess_s8jm3ltvjbwermqsdr035rk6fp6, O_RDWR) failed: No such file or directory (2)"
    string(162) "session_write_close(): Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/www/mysite/data/mod-tmp)


    Оказалось, что этой директории не существовало вовсе, решение - создаём директорию и указываем права на запись.
    Ответ написан
    Комментировать
  • Как реализовать переключение или подмену роли в Yii2?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Переопределил компонент authManager и расширил класс DbManager:
    <?php
    namespace common\rbac;
    
    use Yii;
    use yii\rbac\DbManager as YiiDbManager;
    
    class DbManager extends YiiDbManager
    {
        /**
         * Выбранная роль
         * @var
         */
        protected $currentRole;
    
        /**
         * Немного переопределим инициализацию, чтобы узнать какую роль установил юзер
         */
        public function init()
        {
            parent::init();
    
            if (is_null($this->currentRole)) {
                // Здесь проставим выбранную роль для юзера
               // Пока что вручную, потом данный параметр будет подтягиваться из сессий, к примеру
                $this->currentRole = 'buyer';
            }
        }
    
    
        /**
         * Определим какую из ролей пользователя выдать под текущий запрос
         * @param $userId
         * @return array
         */
        public function getAssignments($userId)
        {
            // Доступные пользователю роли
            $allAssignments = parent::getAssignments($userId);
            // Какую роль он предпочёл в этот раз
            $currentRole = $this->currentRole;
    
            // Найдём в доступных ролях требуемую
            $assignments = array_filter(
                $allAssignments,
                function ($key) use ($currentRole) {
                    return $key === $currentRole;
                },
                ARRAY_FILTER_USE_KEY
            );
    
            // Если роль не нашлась, то используем первую из доступных
            if (empty($assignments)) {
                $assignments = array_shift($allAssignments);
            }
    
            return $assignments;
        }
    }

    Вышеприведённое решение со своей задачей справляется успешно.
    Есть замечания? Буду благодарен за конструктивную критику или альтернативные варианты.
    Ответ написан
    Комментировать
  • Как реализовать middleware-like логику в Yii2?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Появилась идея, переопределить метод runAction:
    public function runAction($id, $params = [])
        {
            \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
            // Если условие истино, то выкидываю ошибку
            if (mt_rand(0,1) === 0) {
                return [
                       'status' => false,
                       'errors' => 'Ошибка'
                ];
            } else {
            // Иначе продолжаем выполнение
                parent::runAction($id, $params);
            }
        }

    Работает как требуется, какие могут быть подводные камни? Рационально/разумно ли такое решение?
    Ответ написан
  • Как выдавать в $_SERVER['REMOTE_ADDR'] IP клиента, а не сервера?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Решение.
    Ubuntu/Debian: отключаем mod_rpaf2 и подключаем mod_remoteip (# a2enmod remoteip) для апача, прописываем следующую конфигурацию:

    <IfModule mod_remoteip.c>
    RemoteIPHeader X-Real-IP
    RemoteIPInternalProxy 127.0.0.1
    RemoteIPInternalProxy [IP вашего хоста]
    RemoteIPInternalProxy [Другой IP вашего хоста]
    </IfModule>
    Ответ написан
    Комментировать
  • Как разрывать соединение с вебсокетом (Socket.IO + Redis + Laravel 5)?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Решение.

    На redis вешалось pmessage для отлавливания эвентов по паттерну. В вашем случае может быть просто message'
    Выглядело так:
    redis.on('pmessage', function (pattern, channel, message) {
        // some code
    });

    В моем случае помогло следующее:
    // вешаем событие не через .on, а через .addListener
    // onPmessage - коллбэк, который вынесен в функцию, в этом же пространстве имен
    redis.addListener('pmessage', onPmessage);

    Cобытие disconnect:
    socket.on('disconnect', function () {
        redis.removeListener('pmessage', onPmessage);
    });


    Проблема исчерпана.

    stackoverflow.com/questions/11617811/how-to-remove...
    Ответ написан
    Комментировать
  • Из за чего не работает корректно парсинг?

    another_dream
    @another_dream
    Backend-разработчик, Laravel/ZF2/Yii2
    Проверяйте после
    foreach (($table_header->children) as $num => $th_row) {
                //  echo "FIRST foreach<br>";
                //var_dump($th_row->plaintext);
                //var_dump($num);
                switch(trim($th_row->plaintext)){
                    case 'Наименование':
                   // case 'Наименование светильника':
                        $numer["name"] = $num;
                        break;
                    case 'Фото':
                    case 'Изображение':
                        $numer['image'] = $num;
                        break;
                    case 'Артикул':
                        $numer['model'] = $num;
                        break;
                }
    
            }

    Что содержится в $numer (дамп) и смотрите, корректно ли все обрабатывается.
    Дальше проходите к циклу foreach ($arr_of_tl as $num => $value) {...}
    Внутри него тоже проверяйте данные, к примеру - $num и $value.

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

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Проблема решена. В одном из мест вызывался тот же метод и там приходил 0, из-за чего сессия не писалась, так как "невалидно". Всем спасибо.
    Ответ написан
    Комментировать
  • Как правильно добавить user_id в базу при использовании Sentinel?

    another_dream
    @another_dream
    Backend-разработчик, Laravel/ZF2/Yii2
    Точно так же создавай в модели User отношение для Post. Получится что-то вроде:
    $post = Sentinel::getUser()->posts()-save($request->all());
    return redirect ('/');

    Возможно, что Сентинель не настроен и используется класс EloquentUser вместо User, в котором и определено отношение с Posts.
    В таком случае нужно в конфиге сентинеля изменить User класс на свой и расширить его от EloquentUser. Подробно - https://github.com/cartalyst/sentinel/wiki/Extendi...

    Читать тут: https://laravel.com/docs/5.1/eloquent#inserting-re...
    Ответ написан
    Комментировать
  • Как реализовать прослушивание событий или каким вообще образом решить задачу?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Andrzej Wielski Станислав Почепко
    Окей, с отлавливанием событий разобрался.
    В модели News имеем следующий код:
    public static function boot()
        {
            parent::boot();
    
            static::creating(function($news)
            {
                dd('Create new item');
            });
        }

    Как вынести код из static::creating(function($news) { ... }) в контроллер отдельный? Например, в CrosspostController@social.

    Подойдет ли следующий вариант?
    // App/Models/News
    public static function boot()
        {
            parent::boot();
    
            static::creating(function($news)
            {
                Event::fire('news.createcrosspost', array($news));
            });
        }
    // App/Providers/EventServiceProvider
    
    protected $listen = [
    		'news.createcrosspost' => [
    			'CrosspostController@social',
    		],
    	];
    Ответ написан
  • Как корректно вернуть 404 код вместе со вьюхой?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    В мануалах ничего дельного не нашел, поэтому написал небольшой хэлпер, аналог abort() именно для 404 статуса.
    function abort404 ($code = 404, $message = '', array $headers = []) {
        header(' ', true, $code);
        return app()->abort($code, $message, $headers);
    }

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

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Собственно решение моего вопроса:
    $update = new Update();
    $update->table($this->table);
    $update->where(array('parent_id' => $parent_id));
    $update->set(array('value' => '1'));
    $this->updateWith($update);
    Ответ написан
    Комментировать
  • Почему не работает JS/jQuery в FF?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Проблема решена. Причина -- привередливость браузера к ошибкам в скриптах. Не работала библиотека jQuery из-за ошибки в предыдущем скрипте. Всем спасибо.
    Ответ написан
    Комментировать
  • Почему не корректно пишутся данные в сессии?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Все дело было в htmlentities. Было написано без кодировки. Указал кодировку(htmlentities($_POST['comment'], ENT_QUOTES, "UTF-8")) и все заработало. Спасибо :)
    Ответ написан
    Комментировать
  • Как правильно обработать jQuery AJAX результат?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    echo json_encode($times);
    jQuery.ajax({
                type: "POST",
                url: "/function/checkTime.php",
                data: ({date:date}),
                dataType:"text",
                success: function(data) {
                    console.log(data);
        }
    });

    Сервер вернул:
    {"0":"1","id":"1","1":"2014-05-24","date":"2014-05-24","2":"16:00","time_start":"16:00","3":"17:00","time_end":"17:00"}

    Как теперь можно обратиться к определенному элементу в этом массиве/объекте? К примеру, alert(data.id) -- не срабатывает :(
    Ответ написан
  • Как, без перезагрузки страницы, передать значение из JS в PHP?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    $.ajax({
      type: 'POST',
      url: 'response.php?action=sample2',
      data: 'name=Andrew&nickname=Aramis',
      success: function(data){
        $('.results').html(data);
      }
    });

    Пример, как можно реализовать. PHP скрипт получит POST данные(name и nickname). Дальше обработка и возвращение результата. Верно?
    Ответ написан
    Комментировать
  • Почему не работает код?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    $('#right').on('click', '.firstcircle, .secondcircle, .thirdcircle, .fourthtcircle', 
        function(){
        var findSelected = $("div.selectedcircle");
        var selectedElementId = $(findSelected).attr('data-slide');
        $('.firstcircle, .secondcircle, .thirdcircle, .fourthtcircle').removeClass('selectedcircle');
        if (selectedElementId == 4) {
            $('.firstcircle').addClass('selectedcircle');}
        else if (selectedElementId == 1) {
            $('.secondcircle').addClass('selectedcircle');}
        else if (selectedElementId == 2) {
            $('.thirdcircle').addClass('selectedcircle');}
        else {$('.fourthcircle').addClass('selectedcircle');}
        });

    @artishok , Теперь верно?
    Ответ написан
    Комментировать
  • Что дописать в скрипте?

    another_dream
    @another_dream Автор вопроса
    Backend-разработчик, Laravel/ZF2/Yii2
    Сам скрипт
    Хэнд-мэйд для имитации переходов:
    $('.circles').on('click', '.firstcircle, .secondcircle, .thirdcircle, .fourthtcircle', function(e) {
        e.preventDefault();
        $('.firstcircle, .secondcircle, .thirdcircle, .fourthtcircle').removeClass('selectedcircle');
        $(this).addClass('selectedcircle');
        });
    Ответ написан
    Комментировать