删除Movie接口
本节我们将添加CRUD接口中的最后一个:Delete操作,以便客户端可以从系统中删除特定的电影。
Method | URL | Handler | 动作 |
---|---|---|---|
GET | /v1/healthcheck | createMovieHandler | 显示应用程序运行状况和版本信息 |
POST | /v1/movies | createMovieHandler | 添加新的电影 |
GET | /v1/movies/:id | showMovieHandler | 根据id查询特定电影 |
PUT | /v1/movies/:id | updateMovieHandler | 更新特定电影 |
DELETE | /v1/movies/:id | deleteMovieHandler | 删除特定电影 |
与API中的其他接口相比,Delete要实现的操作非常简单。
- 如果数据库中存在一个具有URL中提供的id的movie记录,删除相应的记录并向客户端返回一条成功消息。
- 如果movie id不存在,向客户端返回一个404 Not Found响应。
在数据库中删除记录的SQL查询也很简单:
DELETE FROM movies
WHERE id = $1
在这种情况下,SQL查询不返回任何行,因此我们可以使用Go的Exec()方法来执行它。
Exec()的一个好处是它返回一个sql.Result对象,其中包含受影响的行数信息。在我们的场景中,这是非常有用的信息。
- 如果受影响的行数是1,那么我们知道要删除的movie存在于表中,并且现在已经被删除了...所以我们可以向客户端发送成功消息。
- 相反,如果受影响的行数为0,我们知道对应id的movie不存在,向客户端返回404 Not Found响应。
添加Delete接口
让我们继续更新数据库模型中的Delete()方法。本质上,我们希望它执行上面的SQL语句,并在受影响行数为0时返回ErrRecordNotFound错误。像这样:
File:internal/data/movies.go
package data
...
func (m MovieModel) Delete(id int64) error {
//如果movie ID小于1返回ErrRecordNotFound
if id < 1 {
return ErrRecordNotFound
}
//构造SQL query删除movie
query := `
DELETE FROM movies
WHERE id = $1`
//使用Exec()方法执行SQL语句,传入id变量作为占位符参数值。Exec()方法返回sql.Result对象
result, err := m.DB.Exec(query, id)
if err != nil {
return err
}
//调用sql.Result对象的RowsAffected()方法,获取查询影响行数
rowAffected, err := result.RowsAffected()
if err != nil {
return err
}
//如果没有影响行数,说明数据库不存在对应id的记录,返回ErrRecordNotFound
if rowAffected == 0 {
return ErrRecordNotFound
}
return nil
}
完成之后,我们转到cmd/api/movies.go文件,添加一个新的deleteMovieHandler方法。在这个过程中,我们需要从请求URL中读取movie ID,调用刚刚创建的Delete()方法,然后根据Delete()的返回值向客户端返回适当的响应。
File: cmd/api/movies.go
package main
...
func (app *application) deleteMovieHandler(w http.ResponseWriter, r *http.Request) {
//从URL中提取movie ID
id, err := app.readIDParam(r)
if err != nil {
app.notFoundResponse(w, r)
return
}
//从数据库中删除movie,如果没有对应id的movie就返回404
err = app.models.Movies.Delete(id)
if err != nil {
switch {
case errors.Is(err, data.ErrRecordNotFound):
app.notFoundResponse(w, r)
default:
app.serverErrorResponse(w, r, err)
}
return
}
//返回200 Ok以及成功删除
err = app.writeJSON(w, http.StatusOK, envelope{"message": "movie successfully deleted"}, nil)
if err != nil {
app.serverErrorResponse(w, r, err)
}
}
注意:在这里您可能更喜欢发送一个空响应体和一个204 No Content状态码,而不是一个“电影已成功删除”消息。这取决于你的客户端是谁——如果他们是人,那么发送类似于上面的信息是一个很好的选择;如果它们是机器,那么204 No Content响应可能就足够了。
最后,需要在cmd/api/routes.go文件中添加删除movie的路由。
File:cmd/api/routes.go
package main
import (
"github.com/julienschmidt/httprouter"
"net/http"
)
func (app *application) routes() http.Handler {
router := httprouter.New()
router.NotFound = http.HandlerFunc(app.notFoundResponse)
router.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowedResponse)
router.HandlerFunc(http.MethodGet, "/v1/healthcheck", app.healthcheckHandler)
router.HandlerFunc(http.MethodPost, "/v1/movies", app.createMovieHandler)
router.HandlerFunc(http.MethodGet, "/v1/movies/:id", app.showMovieHandler)
router.HandlerFunc(http.MethodPut, "/v1/movies/:id", app.updateMovieHandler)
//添加Delete /v1/movies/:id 接口路由
router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler)
return app.recoverPanic(app.rateLimit(router))
}
好了,重新启动API服务,通过从movie数据库中删除Deadpool来尝试下删除接口(如果您一直跟随本系列文章的操作,它的ID应该是3)。删除操作应该没有任何问题,您应该收到如下的确认消息:
$ curl -X DELETE http://localhost:4000/v1/movies/3
{
"message": "movie successfully deleted"
}
如果您重复相同的请求来删除已经不存在的movie,应该会得到一个404 Not Found响应和错误消息。类似于:
$ curl -X DELETE http://localhost:4000/v1/movies/3
{
"error": "the requested resource could not be found"
}
网友评论