我正在学习创建一个网站,允许用户发布博客,评论博客和回复这些评论。我在comment模式中使用了replies[this]来处理对comment的回复。但是我很难允许用户删除这些回复。下面是我的服务器端代码来删除评论和回复:
// Submit form to leave comment or reply
app.post('/leaveComment/:id', requireLogin, uploadImage.single('commentImage'), async (req, res) => {
try {
const { commentId } = req.query; // Extract commentId from query parameters
const post = await Post.findById({_id: req.params.id});
const commentImage = req.file ? `https://s3.eu-west-2.amazonaws.com/${req.file.originalname}` : null;
const newComment = {
commentUser: req.user, // Use the user object directly
commentBody: req.body.commentBody,
commentImage: commentImage,
dateCreated: new Date()
};
if (!newComment.commentBody || newComment.commentBody.trim() === '') {
// Comment body is empty or contains only whitespace
return res.redirect(`/fullPost/${post._id}`);
}
if (commentId) { // If a commentId exists, it's a reply
const comment = post.comments.id(commentId);
comment.replies.push(newComment);
} else { // It's a top-level comment
post.comments.push(newComment);
}
await post.save();
res.redirect(`/fullPost/${post._id}`);
} catch (err) {
console.log(err);
res.status(500).send("An error occurred");
}
});
// Delete a specific comment or reply
app.get('/deleteComment/:postId/:commentId', requireLogin, async (req, res) => {
try {
const currentUser = req.user;
const { replyId } = req.query;
const foundPost = await Post.findById(req.params.postId);
if (replyId) { // If it's a reply
const comment = foundPost.comments.id(req.params.commentId);
const reply = comment.replies.id(replyId);
// Check if the user is the author of the reply
if (reply.commentUser.toString() !== currentUser._id.toString()) {
return res.status(403).send('Permission denied: You can only delete your own replies.');
}
reply.remove();
} else { // It's a top-level comment
const comment = foundPost.comments.id(req.params.commentId);
// Check if the user is the author of the comment
if (comment.commentUser.toString() !== req.user._id.toString()) {
return res.status(403).send('Permission denied: You can only delete your own comments.');
}
comment.remove();
}
await foundPost.save();
res.status(200).send('Deleted successfully');
} catch (err) {
console.log(err);
res.status(500).send("An error occurred");
}
});
这是我的前端,
<head>
<script src="https://cdn.jsdelivr.net/npm/emoji-mart@latest/dist/browser.js"></script>
</head>
{{#if successMessage}}
<div class="alert alert-success">
{{successMessage}}
</div>
{{/if}}
<div class="media">
<a href="/userProfile/{{post.postUser._id}}">
<img src="{{post.postUser.image}}" class="mr-3" width="64px" alt="">
<span>{{post.postUser.fullname}}</span>
</a>
<div class="media-body">
<h5 class="mt-0">{{post.title}} <small><i class="{{post.icon}}"></i></small></h5>
<img src="{{post.image}}" alt="" width="300" height="auto">
<p>{{post.body}}</p>
<img src="{{image}}" class="mr-3 responsive-img" alt="">
<br>
<small>{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
<div class="comment-and-form" style="display: flex; align-items: center;"></div>
<a href="#" id="comment-icon" onclick="toggleCommentForm('comment_form')">
<i class="fa-solid fa-comment"></i>
</a>
<a href="/deletePost/{{post._id}}"><i class="fa fa-trash post-icons"></i></a>   </a>
</h4>
<form id="comment_form" action="/leaveComment/{{post._id}}" method="POST" enctype="multipart/form-data" class="form-inline" style="display:none; margin-left: 20px;">
<div class="form-group mx-sm-2 mg-2">
<textarea type="text" name="commentBody" id="commentBody1" class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
<span id="charCount1">0/1000</span>
<button type="button" class="btn btn-warning" id="emoji-picker-btn-new1">😀</button>
</div>
<br>
<div class="form-group mx-sm-2 mg-2">
<input type="file" name="commentImage" id="commentImage" class="form-control-file" placeholder="Leave Comment">
</div>
  <button class="btn btn-primary mb-2 mt-2" type="submit">
<i class="fa-brands fa-telegram"></i> Post
</button>
</form>
<hr>
{{#each post.comments}}
<div class="media mt-3">
<a class="mr-3" href="/userProfile/{{commentUser._id}}">
<img src="{{commentUser.image}}" class="mr-3" width="64px" alt="">
<span>{{commentUser.fullname}}</span>
</a>
<div class="media-body">
<p id="comment-body-{{_id}}">{{commentBody}}</p>
<form id="edit-comment-form-{{_id}}" action="#" method="POST" style="display:none">
<textarea name="newCommentBody" id="newCommentBody-{{_id}}" class="form-control emoji-input" rows="4" cols="50" maxlength="1000">{{commentBody}}</textarea>
<button type="button" class="btn btn-warning" id="emoji-picker-btn-{{_id}}">😀</button>
<button type="button" onclick="submitEditComment('{{../post._id}}', '{{_id}}')">Save</button>
<span id="charCount">0/1000</span>
</form>
{{#if commentImage}}
<img src="{{commentImage}}" alt="User uploaded image" width="200" height="auto">
{{/if}}
<br>
<small data-comment-date-{{_id}}="{{getLastMoment dateCreated 'x'}}">{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
{{#if commentUser}}
 
  
<a href="/editComment/{{post._id}}/{{comment._id}}" class="edit-button" onclick="showEditForm(event,'{{_id}}','data-comment-date-{{_id}}')">
<i class="fa fa-pencil post-icons"></i>
</a>
 
 
<a href="#" onclick="deleteComment('{{../post._id}}', '{{_id}}')"><i class="fa fa-trash post-icons"></i></a>
{{/if}}
<!-- Reply button -->
<a href="#" onclick="toggleReplyForm('{{_id}}', '{{../post._id}}')">    Reply</a>
<!-- Reply form -->
<form id="reply-form-{{_id}}" action="/leaveComment/{{../post._id}}?commentId={{_id}}" method="POST" enctype="multipart/form-data" style="display: none;">
<textarea type="text" name="commentBody" id="replycommentBody" class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
<span id="replycharCount">0/1000</span>
<button type="button" class="btn btn-warning" id="emoji-picker-btn-reply">😀</button>
<button class="btn btn-primary mb-2 mt-2" type="submit">
<i class="fa-brands fa-telegram"></i> Post
</button>
</form>
<div class="expand-replies">
<!-- Expand/Collapse button for replies -->
<a href="#" onclick="toggleReplies('{{_id}}')">Expand Replies</a>
<hr>
</div>
</div>
</div>
<div id="replies-container-{{_id}}" style="display: none;" class="replies-container">
{{#each replies}}
<div class="media mt-3">
<a class="mr-3" href="/userProfile/{{commentUser._id}}">
<img src="{{commentUser.image}}" class="mr-3 user-image" alt="user profile">
<span>{{commentUser.fullname}}</span>
</a>
<div class="media-body">
<p id="comment-body-{{_id}}">{{commentBody}}</p>
{{#if commentImage}}
<img src="{{commentUser.image}}" class="mr-3 user-image" alt="user image">
{{/if}}
<br>
<small data-comment-date-{{_id}}="{{getLastMoment dateCreated 'x'}}">{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
 
 
<a href="#" onclick="deleteReply('{{../post._id}}', '{{_id}}', '{{replyId}}')">
<i class="fa fa-trash post-icons"></i>
</a>
</div>
</div>
<hr>
{{/each}}
</div>
</div>
</div>
<hr>
{{/each}}
{{#if post.allowComments}}
<form id="comment_form" action="/leaveComment/{{post._id}}" method="POST" enctype="multipart/form-data" class="form-inline" style="display:none">
<div class="form-group mx-sm-2 mg-2">
<textarea type="text" name="commentBody" id="commentBody2" class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
<span id="charCount2">0/1000</span>
<button type="bu
oup mx-sm-2 mg-2">
<input type="file" name="commentImage" id="commentImage" class="form-control-file">
</div>
<button class="btn btn-primary mb-2 mt-2" type="submit">
<i class="fa fa-comment"></i>
</button>
</form>
<script>
let isPickerOpen = false; // Track whether the emoji picker is open
function toggleCommentForm(formId) {
const commentForm = document.getElementById(formId);
if (commentForm.style.display === 'none' || commentForm.style.display === '') {
// Comment form is closed or not set, so open it
commentForm.style.display = 'block';
} else {
// Comment form is open, so close it
commentForm.style.display = 'none';
}
}
function showEditForm(event, commentId, dateAttribute) {
event.preventDefault();
const FIFTEEN_MINUTES = 15 * 60 * 1000;
const commentDateElement = document.querySelector(`[data-comment-date-${commentId}]`);
const commentDate = parseInt(commentDateElement.getAttribute(`data-comment-date-${commentId}`), 10);
const now = new Date().getTime();
const timeDifference = now - commentDate;
if (timeDifference > FIFTEEN_MINUTES) {
alert('Time limit exceeded, you cannot edit your comment.');
return;
}
document.getElementById('comment-body-' + commentId).style.display = 'none';
document.getElementById('edit-comment-form-' + commentId).style.display = 'block';
document.getElementById(`emoji-picker-btn-${commentId}`).addEventListener('click', function() {
toggleEmojiPicker(`newCommentBody-${commentId}`, `charCount-${commentId}`);
});
}
function submitEditComment(postId, commentId) {
const newCommentBody = document.getElementById(`newCommentBody-${commentId}`).value;
const xhr = new XMLHttpRequest();
xhr.open('POST', `/editComment/${postId}/${commentId}`, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
location.reload(); // Reload the page for a successful edit
} else if (xhr.status === 403) {
// Permission denied, display an error message
alert('Permission denied: You can only edit your own comments.');
} else {
// Handle other error cases and display appropriate messages
alert('An error occurred while editing the comment.');
}
}
};
xhr.send(`commentBody=${encodeURIComponent(newCommentBody)}`);
}
function deleteComment(postId, commentId) {
const xhr = new XMLHttpRequest();
xhr.open('GET', `/deleteComment/${postId}/${commentId}`, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
location.reload(); // Reload the page for successful deletion
} else if (xhr.status === 403) {
// Permission denied, display an error message
alert('Permission denied: You can only delete your own comments.');
} else {
// Handle other error cases and display appropriate messages
alert('An error occurred while deleting the comment.');
}
}
};
xhr.send();
}
function toggleEmojiPicker(inputId, charCountId) {
if (isPickerOpen) {
const existingPicker = document.getElementById('emoji-picker');
if (existingPicker) {
existingPicker.remove();
}
isPickerOpen = false;
} else {
const pickerOptions = {
onEmojiSelect: function(emoji) {
const emojiInput = document.getElementById(inputId);
emojiInput.value += emoji.native;
const charCountSpan = document.getElementById(charCountId);
charCountSpan.innerText = `${emojiInput.value.length}/1000`;
}
};
const picker = new EmojiMart.Picker(pickerOptions);
picker.id = 'emoji-picker';
picker.style.position = 'fixed';
picker.style.bottom = '0px';
picker.style.left = '0px';
document.body.appendChild(picker);
isPickerOpen = true;
// Make the picker draggable
let isDragging = false;
let offsetX, offsetY;
picker.addEventListener('mousedown', function(event) {
isDragging = true;
offsetX = event.clientX - picker.getBoundingClientRect().left;
offsetY = event.clientY - picker.getBoundingClientRect().top;
});
window.addEventListener('mousemove', function(event) {
if (isDragging) {
const x = event.clientX - offsetX;
const y = event.clientY - offsetY;
picker.style.left = x + 'px';
picker.style.top = y + 'px';
picker.style.bottom = 'auto'; // Reset 'bottom' to allow 'top' to take effect
}
});
window.addEventListener('mouseup', function() {
isDragging = false;
});
}
}
function initializeTextareaAndCharCount(textareaId, charCountId) {
const textarea = document.getElementById(textareaId);
const charCountSpan = document.getElementById(charCountId);
textarea.addEventListener('input', function() {
const charCount = this.value.length;
charCountSpan.innerText = `${charCount}/1000`;
});
}
initializeTextareaAndCharCount('replycommentBody', 'replycharCount');
document.getElementById('emoji-picker-btn-reply').addEventListener('click', function() {
toggleEmojiPicker('replycommentBody', 'replycharCount');
});
initializeTextareaAndCharCount('commentBody1', 'charCount1');
document.getElementById('emoji-picker-btn-new1').addEventListener('click', function() {
toggleEmojiPicker('commentBody1', 'charCount1');
});
initializeTextareaAndCharCount('commentBody2', 'charCount2');
document.getElementById('emoji-picker-btn-new2').addEventListener('click', function() {
toggleEmojiPicker('commentBody2', 'charCount2');
});
// Function to toggle the reply form for a comment
function toggleReplyForm(commentId, postId) {
event.preventDefault();
const replyForm = document.getElementById(`reply-form-${commentId}`);
replyForm.style.display = replyForm.style.display === 'none' ? 'block' : 'none';
}
// Function to toggle the visibility of replies
function toggleReplies(commentId) {
event.preventDefault();
const repliesContainer = document.getElementById(`replies-container-${commentId}`);
repliesContainer.style.display = repliesContainer.style.display === 'none' ? 'block' : 'none';
}
</script>
{{else}}
<p class="red"> Comments are not allowed </p>
{{/if}}
</div>
</div>
每次我试图删除回复时,都会收到404错误。我可以删除评论,这只是回复。我已经尝试了许多事情,如改变路由和功能,包括replyId,但它只是不断给404错误。知道为什么不管用吗
1条答案
按热度按时间um6iljoc1#
下面是我从你提供的代码片段中可以告诉你的:
删除此路由中的注解是可行的,因为
/deleteComment/:postId/:commentId
接收postId
和commentId
,这是删除注解所需的唯一两个参数。当你试图删除一个回复时,你的服务器端代码没有收到所需的replyId
,因此出现了404。此外,当你有一个GET路由时,它需要在前端和后端都完全匹配,才能正常工作。这就是为什么有两个GET请求将解决你的问题。一个删除评论
/deleteComment/:postId/:commentId
,一个删除回复/deleteReply/:postId/:commentId/:replyId
。有多种方法可以做到这一点。这里有一个方法。
服务器端:
前端:
现在确保你使用两个不同的锚点,一个删除回复,一个删除帖子:
删除备注:
删除回复(确保使用正确的replyId变量)
同样,您将面临404错误,因为
rpleyId
在当前代码中未定义,因为它不是URL查询的一部分。这也是一个很好的做法,当你使用一个路由来删除一些东西使用一个路由。
如果你需要更多的帮助,我很乐意帮助:)