حذف یک فیلم
در این فصل، آخرین نقطه پایانی CRUD را اضافه خواهیم کرد تا یک کلاینت بتواند یک فیلم مشخص را از سیستم ما حذف کند.
| عملیات | هندلر | الگوی URL | متد |
|---|---|---|---|
| نمایش اطلاعات برنامه | healthcheckHandler | /v1/healthcheck | GET |
| ایجاد یک فیلم جدید | createMovieHandler | /v1/movies | POST |
| نمایش جزئیات یک فیلم مشخص | showMovieHandler | /v1/movies/:id | GET |
| بهروزرسانی جزئیات یک فیلم مشخص | updateMovieHandler | /v1/movies/:id | PUT |
| حذف یک فیلم مشخص | deleteMovieHandler | /v1/movies/:id | DELETE |
در مقایسه با سایر نقاط پایانی در API ما، رفتاری که میخواهیم در اینجا پیادهسازی کنیم کاملاً ساده است.
- اگر فیلمی با
idمشخصشده در URL در پایگاه داده وجود داشته باشد، میخواهیم رکورد مربوطه را حذف کرده و یک پیام موفقیت به کلاینت برگردانیم. - اگر فیلم با آن
idوجود نداشته باشد، میخواهیم یک پاسخ404 Not Foundبه کلاینت برگردانیم.
کوئری SQL برای حذف رکورد در پایگاه داده ما نیز ساده است:
DELETE FROM movies WHERE id = $1
در اینجا کوئری SQL هیچ ردیفی برنمیگرداند، بنابراین استفاده از متد Exec() زبان Go برای اجرای آن مناسب است.
یکی از نکات خوب متد Exec() این است که یک مقدار sql.Result برمیگرداند که حاوی اطلاعاتی درباره تعداد ردیفهایی است که کوئری تحت تأثیر قرار داده است. در سناریوی ما، این اطلاعات واقعاً مفید است.
- اگر تعداد ردیفهای تحت تأثیر
1باشد، میدانیم که فیلم در جدول وجود داشته و اکنون حذف شده است... پس میتوانیم یک پیام موفقیت به کلاینت ارسال کنیم. - در مقابل، اگر تعداد ردیفهای تحت تأثیر
0باشد، میدانیم که هیچ فیلمی با آنidدر لحظه تلاش برای حذف وجود نداشته است و میتوانیم یک پاسخ404 Not Foundبه کلاینت ارسال کنیم.
افزودن نقطه پایانی جدید
بیایید متد Delete() در مدل پایگاه داده خود را بهروزرسانی کنیم. در اصل، میخواهیم این متد کوئری SQL بالا را اجرا کند و در صورتی که تعداد ردیفهای تحت تأثیر 0 باشد، خطای ErrRecordNotFound برگرداند. به این صورت:
package data ... func (m MovieModel) Delete(id int64) error { // Return an ErrRecordNotFound error if the movie ID is less than 1. if id < 1 { return ErrRecordNotFound } // Construct the SQL query to delete the record. query := ` DELETE FROM movies WHERE id = $1` // Execute the SQL query using the Exec() method, passing in the id variable as // the value for the placeholder parameter. The Exec() method returns a sql.Result // value. result, err := m.DB.Exec(query, id) if err != nil { return err } // Call the RowsAffected() method on the sql.Result value to get the number of rows // affected by the query. rowsAffected, err := result.RowsAffected() if err != nil { return err } // If no rows were affected, we know that the movies table didn't contain a record // with the provided ID at the moment we tried to delete it. In that case we // return an ErrRecordNotFound error. if rowsAffected == 0 { return ErrRecordNotFound } return nil }
پس از اتمام این کار، بیایید به فایل cmd/api/movies.go برویم و یک متد جدید deleteMovieHandler اضافه کنیم. در این متد باید شناسه فیلم را از URL درخواست بخوانیم، متد Delete() که همین الان ساختیم را فراخوانی کنیم و — بر اساس مقدار برگشتی از Delete() — پاسخ مناسب را به کلاینت ارسال کنیم.
package main ... func (app *application) deleteMovieHandler(w http.ResponseWriter, r *http.Request) { // Extract the movie ID from the URL. id, err := app.readIDParam(r) if err != nil { app.notFoundResponse(w, r) return } // Delete the movie from the database, sending a 404 Not Found response to the // client if there isn't a matching record. 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 } // Return a 200 OK status code along with a success message. err = app.writeJSON(w, http.StatusOK, envelope{"message": "movie successfully deleted"}, nil) if err != nil { app.serverErrorResponse(w, r, err) } }
در نهایت، باید مسیر جدید را در فایل cmd/api/routes.go خود متصل کنیم:
package main ... 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) // Add the route for the DELETE /v1/movies/:id endpoint. router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler) return app.recoverPanic(router) }
بسیار خب، بیایید API را مجدداً راهاندازی کنیم و این را با حذف فیلم Deadpool از پایگاه داده فیلمهایمان آزمایش کنیم (اگر همراه ما بودید، این فیلم باید شناسه 3 داشته باشد). عملیات حذف باید بدون هیچ مشکلی کار کند و باید پیام تأیید را به این صورت دریافت کنید:
$ curl -X DELETE localhost:4000/v1/movies/3
{
"message": "movie successfully deleted"
}
اگر درخواست حذف یکسانی برای فیلمی که قبلاً حذف شده تکرار کنید، اکنون باید یک پاسخ 404 Not Found و پیام خطا دریافت کنید. مشابه این:
$ curl -X DELETE localhost:4000/v1/movies/3
{
"error": "the requested resource could not be found"
}