Golang新手如何进行代码审查
Golang新手如何进行代码审查 最近我开始学习Go语言,正在编写一个管理联系人信息的小型Web应用程序。如果您能查看以下代码并给我一些改进建议,我将不胜感激。这是一个提供基本CRUD操作的mux路由器。例如,让我困扰的一点是重复的身份验证检查。
package server
import (
"log"
"encoding/json"
"errors"
"net/http"
"github.com/gorilla/mux"
"github.com/cretzel/dopi-contacts/pkg"
)
type contactRouter struct {
contactService root.ContactService
}
func NewContactRouter(contactService root.ContactService, router *mux.Router) *mux.Router {
contactRouter := contactRouter{contactService}
router.HandleFunc("/contacts", contactRouter.getContacts).Methods("GET")
router.HandleFunc("/contacts", contactRouter.createContact).Methods("POST")
router.HandleFunc("/contacts/{id}", contactRouter.updateContact).Methods("PUT")
router.HandleFunc("/contacts/{id}", contactRouter.deleteContact).Methods("DELETE")
return router
}
func (router *contactRouter) getContacts(response http.ResponseWriter, request *http.Request) {
username, err := getCurrentUser(request)
if err != nil {
Error(response, http.StatusForbidden, err.Error())
return
}
contacts, err := router.contactService.GetContacts(username)
if err != nil {
Error(response, http.StatusInternalServerError, err.Error())
return
}
Json(response, http.StatusOK, contacts)
}
func (router *contactRouter) createContact(response http.ResponseWriter, request *http.Request) {
username, err := getCurrentUser(request)
if err != nil {
Error(response, http.StatusForbidden, err.Error())
return
}
contact, err := decodeContact(request)
if err != nil {
Error(response, http.StatusBadRequest, "Invalid request payload")
return
}
responseContact, err := router.contactService.CreateContact(username, &contact)
if err != nil {
Error(response, http.StatusInternalServerError, err.Error())
return
}
Json(response, http.StatusCreated, responseContact)
}
func (router *contactRouter) updateContact(response http.ResponseWriter, request *http.Request) {
username, err := getCurrentUser(request)
if err != nil {
Error(response, http.StatusForbidden, err.Error())
return
}
vars := mux.Vars(request)
id := vars["id"]
contact, err := decodeContact(request)
if err != nil {
Error(response, http.StatusBadRequest, "Invalid request payload")
return
}
err = router.contactService.UpdateContact(username, id, &contact)
if err != nil {
Error(response, http.StatusInternalServerError, err.Error())
return
}
Json(response, http.StatusOK, nil)
}
func (router *contactRouter) deleteContact(response http.ResponseWriter, request *http.Request) {
username, err := getCurrentUser(request)
if err != nil {
Error(response, http.StatusForbidden, err.Error())
return
}
vars := mux.Vars(request)
id := vars["id"]
err = router.contactService.DeleteContact(username, id)
if err != nil {
Error(response, http.StatusInternalServerError, err.Error())
return
}
Json(response, http.StatusOK, "")
}
func decodeContact(request *http.Request) (root.Contact, error) {
var contact root.Contact
if request.Body == nil {
return contact, errors.New("no request body")
}
decoder := json.NewDecoder(request.Body)
err := decoder.Decode(&contact)
return contact, err
}
func getCurrentUser(request *http.Request) (string, error) {
username := request.Header.Get("X-User")
log.Println("Username", username)
if (username == "") {
return username, errors.New("no username found")
}
return username, nil
}
更多关于Golang新手如何进行代码审查的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你可以使用中间件来处理身份验证。例如,可以使用中间件验证用户是否已正确认证,然后将其保存到"上下文"中:
httpError(w, unauthorizedError)
return
}
u := &manager.User{AccessToken: token}
if !u.ReadUserByToken(env.DB) {
httpError(w, unauthorizedError)
return
}
ctx := context.WithValue(r.Context(), contextAuthUser, u)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
之后,你可以从处理程序中获取该用户:
"net/http"
"strconv"
"strings"
"github.com/gchumillas/photomanager/manager"
"github.com/gorilla/mux"
)
// GetCategories获取所有分类
func (env *Env) GetCategories(w http.ResponseWriter, r *http.Request) {
u := getAuthUser(r)
params := mux.Vars(r)
parentCatID := params["catID"]
sortCols := strings.Split(getParam(r, "sort", "name"), ",")
for _, col := range sortCols {
pos := strings.IndexRune(col, '-')
str := col
if pos > -1 {
str = col[pos+1:]
unauthorizedError = httpStatus{401, "Not authorized."}
docNotFoundError = httpStatus{404, "Document not found."}
)
// Env包含公共变量,例如数据库访问等
type Env struct {
DB *mgo.Database
MaxItemsPerPage int
}
func getAuthUser(r *http.Request) *manager.User {
return r.Context().Value(contextAuthUser).(*manager.User)
}
func parsePayload(w http.ResponseWriter, r *http.Request, payload interface{}) {
dec := json.NewDecoder(r.Body)
if err := dec.Decode(payload); err != nil {
httpError(w, payloadError)
return
}
我之前做这个项目也是为了学习GO 🙂
更多关于Golang新手如何进行代码审查的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是针对您提供的Go代码的审查意见,重点关注重复的身份验证检查问题以及其他潜在改进点。我会直接给出代码示例来展示如何优化。
1. 消除重复的身份验证检查
您在每个处理函数中都调用了getCurrentUser来验证用户身份,这违反了DRY(Don’t Repeat Yourself)原则。可以通过中间件来统一处理身份验证。
示例代码:身份验证中间件
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(response http.ResponseWriter, request *http.Request) {
username, err := getCurrentUser(request)
if err != nil {
Error(response, http.StatusForbidden, err.Error())
return
}
// 将用户名存储在请求上下文中,供后续处理函数使用
ctx := context.WithValue(request.Context(), "username", username)
next.ServeHTTP(response, request.WithContext(ctx))
}
}
// 修改路由注册,应用中间件
func NewContactRouter(contactService root.ContactService, router *mux.Router) *mux.Router {
contactRouter := contactRouter{contactService}
router.HandleFunc("/contacts", authMiddleware(contactRouter.getContacts)).Methods("GET")
router.HandleFunc("/contacts", authMiddleware(contactRouter.createContact)).Methods("POST")
router.HandleFunc("/contacts/{id}", authMiddleware(contactRouter.updateContact)).Methods("PUT")
router.HandleFunc("/contacts/{id}", authMiddleware(contactRouter.deleteContact)).Methods("DELETE")
return router
}
// 在处理函数中从上下文中获取用户名
func (router *contactRouter) getContacts(response http.ResponseWriter, request *http.Request) {
username := request.Context().Value("username").(string)
contacts, err := router.contactService.GetContacts(username)
if err != nil {
Error(response, http.StatusInternalServerError, err.Error())
return
}
Json(response, http.StatusOK, contacts)
}
2. 统一错误处理
当前代码中每个处理函数都重复进行错误检查,可以提取一个辅助函数来简化。
示例代码:错误处理辅助函数
func handleError(response http.ResponseWriter, err error, defaultMessage string, statusCode int) {
if err != nil {
Error(response, statusCode, err.Error())
} else {
Error(response, statusCode, defaultMessage)
}
}
// 在处理函数中使用
func (router *contactRouter) getContacts(response http.ResponseWriter, request *http.Request) {
username := request.Context().Value("username").(string)
contacts, err := router.contactService.GetContacts(username)
if err != nil {
handleError(response, err, "Failed to retrieve contacts", http.StatusInternalServerError)
return
}
Json(response, http.StatusOK, contacts)
}
3. 改进JSON解码和响应
decodeContact函数可以更健壮地处理JSON解码错误,并统一响应格式。
示例代码:增强的JSON解码
func decodeContact(request *http.Request) (root.Contact, error) {
var contact root.Contact
if request.Body == nil {
return contact, errors.New("no request body")
}
defer request.Body.Close()
decoder := json.NewDecoder(request.Body)
decoder.DisallowUnknownFields() // 防止未知字段导致错误
err := decoder.Decode(&contact)
if err != nil {
return contact, fmt.Errorf("invalid JSON: %v", err)
}
return contact, nil
}
4. 使用常量代替硬编码字符串
将硬编码的字符串(如状态消息)提取为常量,提高可维护性。
示例代码:定义常量
const (
ErrNoUsername = "no username found"
ErrInvalidPayload = "Invalid request payload"
)
func getCurrentUser(request *http.Request) (string, error) {
username := request.Header.Get("X-User")
log.Println("Username", username)
if username == "" {
return username, errors.New(ErrNoUsername)
}
return username, nil
}
5. 优化日志记录
当前代码使用log.Println记录用户名,建议使用结构化的日志库(如logrus或zap)以便于生产环境调试。
示例代码:使用logrus
import "github.com/sirupsen/logrus"
var logger = logrus.New()
func getCurrentUser(request *http.Request) (string, error) {
username := request.Header.Get("X-User")
logger.WithField("username", username).Info("User authentication attempt")
if username == "" {
return username, errors.New(ErrNoUsername)
}
return username, nil
}
这些修改将减少代码重复,提高可读性和可维护性,同时保持功能不变。

