发 GET 请求不应该改变状态吗?

大多数时候我认同这种观点,但是偶尔有时候也会“叛逆”一下。

以前的博客在点击“发表新文章”的时候,不会有任何“新建”动作,会等到第一次提交保存的时候才会真实创建。 这样很好,可以保证每个“文章编号”都是存在的、有意义的、连续的,对我这种有洁癖的人很适用。

但是有时候也会遇到麻烦:

  1. 文章没真实创建,第一次提交保存之前的上传的图片不知道保存在哪里。

    为了引用图片方便,我的图片是跟文章编号绑定的。而不像多数大家一样用图床、不分文章。

类似的小问题挺多,总之就是要区分是“新发表”还是“更新已有的”。

我现在改进了这个做法:

  1. 在点击“发表新文章”时,自动创建一篇空文章;
  2. 拿到文章编号,重定向到“/editor?id=1677”;

这就相当于“更新”一篇已经存在的文章,代码里面少了很多类似“判断文章是否存在”的代码,逻辑清晰了很多。

当然,这也引入了另外一些问题:

  1. 经常会产生“[私密]无标题”这样的尚未发表的文章。目前没有想到很好的办法解决这个问题,那就先拖延。
  2. 按理说第一次编辑才是发表新文章,但是文章的发表时间却是创建空文章时候的。应该怎么区分?

等等,我怎么感觉没有在说 GET 应不应该改变状态的事?(逃……

2025-5-18 13:16:18

找到了,代码里面有这样一行:

1
<p><button onclick="location.href='editor?new=1'">发表文章</button></p>

以及实现部分

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
func (a *Admin) getEditor(w http.ResponseWriter, r *http.Request) {
	if isNew := r.URL.Query().Get(`new`) == `1`; isNew {
		post := utils.Must1(a.svc.CreatePost(r.Context(),
			&proto.Post{
				Type:       `post`,
				SourceType: `markdown`,
				Source:     fmt.Sprintf("# %s\n\n", models.Untitled),
			},
		))
		args := urlpkg.Values{}
		args.Set(`id`, fmt.Sprint(post.Id))
		url := a.prefixed(`editor`) + `?` + args.Encode()
		http.Redirect(w, r, url, http.StatusFound)
		return
	}

我为了懒得写 <form>POST 请求,所以“发表文章”实际上是个 GET 请求。感觉不是一个好习惯。

碎碎念 开发笔记