2.12 修改与删除博客

90 阅读2分钟

这里先说下上节作业,将博客按创建时间倒序显示。

修改posts.service.ts的formatPosts方法,在最后添加排序:

 posts.sort((a, b) => {
   if (a.createTime > b.createTime) {
     return -1;
   }
   return 1;
 });

这时在页面上查看效果:

image.png

本节将处理博客的修改页面和删除操作。

修改页面

edit.ejs

新建views/posts/edit.ejs,内容如下:

<%- include('../header') %>

<div class="ui grid">
  <div class="four wide column">
    <a class="avatar" href="/posts?userId=<%= user.id %>" data-title="<%= user.name %> | <%= ({m: '男', f: '女', x: '保密'})[user.gender] %>" data-content="<%= user.bio %>">
      <img class="avatar" src="/static/img/<%= user.avatar %>">
    </a>
  </div>

  <div class="eight wide column">
    <form class="ui form segment" method="post" action="/posts/<%= post.id %>/edit">
      <div class="field required">
        <label>标题</label>
        <input type="text" name="title" value="<%= post.title %>" maxlength="100" required>
      </div>
      <div class="field required">
        <label>内容</label>
        <textarea name="content" rows="15" maxlength="1000" required><%= post.content %></textarea>
      </div>
      <input type="submit" class="ui button" value="发布">
    </form>
  </div>
</div>

<%- include('../footer') %>

post-content.ejs

修改views/components/post-content.ejs,在<span>浏览(<%= post.pv %>)</span>之后,增加编辑和删除两个按钮:

<% if (user && post.author.id && user.id===post.author.id) { %>
  <div class="ui inline dropdown">
    <div class="text"></div>
    <i class="dropdown icon"></i>
    <div class="menu">
      <div class="item"><a href="/posts/<%= post.id %>/edit">编辑</a></div>
      <div class="item"><a href="/posts/<%= post.id %>/remove">删除</a></div>
    </div>
  </div>
  <% } %>

刷新页面,在浏览的右下角有个按钮,点开会弹出窗口:

image.png

post.dto.ts

修改src/posts/posts.dto.ts,添加一个更新的dto:

export class UpdatePostDto {
  @IsString()
  @IsOptional()
  @MaxLength(100)
  title?: string;

  @IsString()
  @IsOptional()
  @MaxLength(1000)
  content?: string;
}

posts.service.ts

src/posts/posts.service.ts,增加一个update方法:

 update(id: string, params: UpdatePostDto) {
    return this.model.findByIdAndUpdate(id, params);
 }

posts.controller.ts

src/posts/posts.controller.ts增加渲染GET方法和更新的POST方法:

 @UseGuards(SSOGuard)
 @Get("/:id/edit")
  async editPage(@Params("id") id: string, @Render() render: Render) {
    const post = await this.postsService.findById(id, {
      isWithUserInfo: true,
    });
    return render("posts/edit", { post });
  }

  @Post("/:id/edit")
  @UseGuards(SSOGuard)
  async updatePost(
    @Params("id") id: string,
    @Form() params: UpdatePostDto,
    @Res() res: Response,
    @Flash() flash: Flash,
  ) {
    await this.postsService.update(id, params);
    flash("success", "更新成功");
    // 编辑成功后跳转到文章页面
    res.redirect("/posts/" + id);
  }

这里更新操作为什么没有选择使用PUT,而是用的POST呢?

原因是我们在ejs中直接用Form表单提交,它目前只支持GET和POST。

事实上,实际业务中很少使用DELETE和PUT,虽然他们更有语义,但因为某些web软件认为二者有安全漏洞,默认会禁止相应操作(zf部门的网关层更是常见),要使用的话又需要额外的处理,增加了运维的复杂性。

验证

点击编辑按钮,修改标题或内容,提交后刷新页面看是否数据已经变化了。

删除接口

删除接口要容易的多。

src/posts/posts.service.ts,增加一条:

deleteById(id: string) {
    return this.model.findByIdAndDelete(id);
 }

修改src/posts/posts.controller.ts,新增一个GET方法:

  @UseGuards(SSOGuard)
  @Get("/:id/remove")
  async remove(
    @Params("id") id: string,
    @UserParam() user: UserInfo,
    @Res() res: Response,
    @Flash() flash: Flash,
  ) {
    const post = await this.postsService.findById(id);
    if (!post) {
      throw new NotFoundException(`未找到id为${id}的博客`);
    }
    if (post.userId !== user.id) {
      throw new ForbiddenException(`您没有权限删除该博客`);
    }
    await this.postsService.deleteById(id);

    flash("success", "删除成功");
    // 删除成功后跳转到主页
    res.redirect("/posts");
  }

注意:前文说过,不推荐实际中使用GET进行操作。

请自行验证。

作业

不知道你有没有注意,我们的views/posts/edit.ejs与create.ejs有太多的相似之处,你可以试着把它们提取一个子组件,提高代码复用性。