В последнее время я работаю над системой уведомлений.Я дал возможность создавать их и пользователю, который их получает, читать их, и теперь я хочу, чтобы пользователь мог их отклонить и удалить.Я хочу обрабатывать удаление там асинхронно, чтобы пользователю не приходилось обновлять страницу каждый раз, когда он отклоняет уведомление.
Наименее важные уведомления остаются только во время одного запроса, поэтому я обработал их с помощью flashbag сессии, и, по щелчку, просто удалите их из DOM в javascript.
Но некоторые более важные сообщения остаются, пока пользователь не щелкнет по ним.Поэтому в javascript, когда пользователь щелкает их, я отправляю Ajax-запрос контроллеру, который должен удалить уведомление.Проблема в том, что контроллер не может получить доступ к подключенному пользователю!Я пытался использовать $this->getUser()
и $tokenInterace->getToken()
, но оба возвращают null
.
. Доступ к пользователю позволил бы мне проверить, действительно ли он владеет уведомлением, а не просто набрать случайный URL, пытаясьподавить чье-то уведомление.Это также позволило бы очистить все уведомления пользователя (как еще одна функция).
Здесь приведены все настройки и код, которые, я думаю, могут относиться к этой проблеме.
Symfony
# config/packages/framework.yaml
framework:
session:
handler_id: ~
SecurityBundle
# config/packages/security.yaml
security:
# ...
firewalls:
dev:
pattern: ^/(_(profiler|wdt))/
security: false
login:
pattern: ^/login$
anonymous: true
main:
pattern: ^/
anonymous: false
form_login:
# ...
remember_me:
secret: '%env(APP_SECRET)%'
lifetime: 604800
access_control:
# - { path: ^/api, role: ROLE_USER }
# If I uncomment this line, Ajax request get redirected to login page
# ...
FOSUserBundle
# config/packages/fos_user.yaml
fos_user:
db_driver: orm
firewall_name: main
user_class: App\Entity\User\User
from_email:
address: '%env(MAILER_URL)%'
sender_name: '%env(MAILER_URL)%'
Контроллер
// The controller is imported with options prefix="/api", name_prefix="api_" and expose=true
// (name_prefix is new from Symfony 4.1, it permits to automatically prefix all imported route names)
/**
* @Route("/notification", name="notification_")
*/
class NotificationApiController extends BaseController
{
/**
* @Route("/{id}", name="delete", requirements={"id"="\d+"})
* @Method({"DELETE"})
*/
public function delete(Notification $notification, TranslatorInterface $translator)
{
if ($notification->getUser() !== $this->getUser()) {
return $this->createJsonResponse([
'error' => $translator->trans('error.notification.not_owner'),
], 403);
}
$em = $this->getDoctrine()->getEntityManager();
$em->remove($notification);
$em->flush();
return $this->createJsonResponse();
}
/**
* @Route("/clear", name="clear")
* @Method({"DELETE"})
*/
public function clear(NotificationRepository $notificationRepository, TranslatorInterface $translator)
{
$user = $this->getUser();
if ($user === null) {
return $this->createJsonResponse([
'error' => $translator->trans('error.notification.not_user'),
]);
}
$notificationRepository->clearForUser($user);
return $this->createJsonResponse();
}
}
Javascript
// Notifications are in the header
const $header = document.querySelector('header');
$header.addEventListener('click', (e) => {
const $notif = e.target.closest('.notif');
if ($notif !== null) {
// Parse the notification id
const id = +$notif.getAttribute('data-id');
if (id !== null) {
const route = Routing.generate('api_notification_delete', { id });
// I don't care about browser support
fetch(route, { method: 'DELETE' })
.then((response) => {
// check for errors
return response.json();
})
.then((response) => {
// remove notification from DOM;
})
}
}
});
Я почти уверен, что проблема не в истечении сеанса.Или, может быть, сессия работает иначе для асинхронного запроса?(Это было бы очень странно)
Кто-нибудь когда-либо сталкивался с проблемой, или есть идея, как ее исправить?Если вам нужно что-то еще, не стесняйтесь спрашивать!
РЕДАКТИРОВАТЬ
Я нашел причину проблемы.Это «выборка».Fetch API по умолчанию не использует куки.Чтобы использовать куки, вы должны передавать { credentials: 'same-origin' }
в качестве параметра для выборки каждый раз, когда вы используете его (как я обнаружил здесь ).
Надеюсь, это может кому-то помочь однажды!