عند معالجة طلب HTTP الوارد ، يلزم عدد كبير من الإجراءات ، مثل:
- تسجيل طلب HTTP وارد
 - التحقق من صحة طريقة HTTP
 - المصادقة (أساسية ، MS AD ، ...)
 - التحقق من الرمز (إذا لزم الأمر)
 - قراءة نص الطلب الوارد
 - قراءة رأس الطلب الوارد
 - في الواقع معالجة الطلب وتوليد رد
 - قم بتثبيت HSTS Strict-Transport-Security
 - تعيين نوع المحتوى للاستجابة الصادرة
 - تسجيل استجابة HTTP الصادرة
 - رأس سجل الاستجابة الصادرة
 - سجل هيئة الاستجابة الصادرة
 - معالجة الخطأ وتسجيل
 - تأجيل معالجة الاسترداد للتعافي بعد الذعر المحتمل
 
معظم هذه الإجراءات نموذجية ولا تعتمد على نوع الطلب ومعالجته.
للتشغيل المنتج ، من الضروري لكل معالجة توفير معالجة الأخطاء وتسجيل الدخول.
تكرار كل هذا في كل معالج HTTP غير فعال للغاية.
حتى إذا قمت بوضع جميع التعليمات البرمجية في وظائف فرعية منفصلة ، فلا يزال بإمكانك الحصول على حوالي 80-100 سطر من التعليمات البرمجية لكل معالج HTTP دون مراعاة المعالجة الفعلية للطلب وتشكيل الاستجابة .
فيما يلي وصف للطريقة التي أستخدمها لتبسيط كتابة معالجات HTTP في Golang بدون استخدام مولدات التعليمات البرمجية ومكتبات الجهات الخارجية.
backend Golang
.
, , — , - .
HTTP
UML HTTP EchoHandler.
, , :
- defer . UML — RecoverWrap.func1, .
 - . HTTP handler. UML Process — .
 - HTTP handler. UML EchoHandler.func1 — .
 

.
HTTP EchoHandler, "" .
, EchoHandler, ( RecoverWrap), EchoHandler.
router.HandleFunc("/echo", service.RecoverWrap(http.HandlerFunc(service.EchoHandler))).Methods("GET")
RecoverWrap .
defer func() EchoHandler.
func (s *Service) RecoverWrap(handlerFunc http.HandlerFunc) http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        
        defer func() {
            var myerr error
            r := recover()
            if r != nil {
                msg := "HTTP Handler recover from panic"
                switch t := r.(type) {
                case string:
                    myerr = myerror.New("8888", msg, t)
                case error:
                    myerr = myerror.WithCause("8888", msg, t)
                default:
                    myerr = myerror.New("8888", msg)
                }
                
                s.processError(myerr, w, http.StatusInternalServerError, 0)
            }
        }()
        
        if handlerFunc != nil {
            handlerFunc(w, r)
        }
    })
}
, EchoHandler. , HTTP Process .
func (s *Service) EchoHandler(w http.ResponseWriter, r *http.Request) {
    
    _ = s.Process("POST", w, r, func(requestBuf []byte, reqID uint64) ([]byte, Header, int, error) {
        header := Header{} 
        
        for key := range r.Header {
            header[key] = r.Header.Get(key)
        }
        
        return requestBuf, header, http.StatusOK, nil
    })
}
HTTP Process. :
- method string — HTTP HTTP
 - w http.ResponseWriter, r *http.Request —
 - fn func (requestBuf [] byte، reqID uint64) ([] byte، Header، int، error) - وظيفة المعالجة نفسها ، تتلقى مخزنًا مؤقتًا للطلبات الواردة ورقم طلب HTTP فريدًا (لأغراض التسجيل) ، تُرجع مخزنًا مؤقتًا للاستجابة الصادرة ، رأس الاستجابة الصادرة ، حالة HTTP والخطأ.
 
func (s *Service) Process(method string, w http.ResponseWriter, r *http.Request, fn func(requestBuf []byte, reqID uint64) ([]byte, Header, int, error)) error {
    var myerr error
    
    reqID := GetNextRequestID()
    
    if s.logger != nil {
        _ = s.logger.LogHTTPInRequest(s.tx, r, reqID) 
        mylog.PrintfDebugMsg("Logging HTTP in request: reqID", reqID)
    }
    
    mylog.PrintfDebugMsg("Check allowed HTTP method: reqID, request.Method, method", reqID, r.Method, method)
    if r.Method != method {
        myerr = myerror.New("8000", "HTTP method is not allowed: reqID, request.Method, method", reqID, r.Method, method)
        mylog.PrintfErrorInfo(myerr)
        return myerr
    }
    
    mylog.PrintfDebugMsg("Check authentication method: reqID, AuthType", reqID, s.cfg.AuthType)
    if (s.cfg.AuthType == "INTERNAL" || s.cfg.AuthType == "MSAD") && !s.cfg.UseJWT {
        mylog.PrintfDebugMsg("JWT is of. Need Authentication: reqID", reqID)
        
        username, password, ok := r.BasicAuth()
        if !ok {
            myerr := myerror.New("8004", "Header 'Authorization' is not set")
            mylog.PrintfErrorInfo(myerr)
            return myerr
        }
        mylog.PrintfDebugMsg("Get Authorization header: username", username)
        
        if myerr = s.checkAuthentication(username, password); myerr != nil {
            mylog.PrintfErrorInfo(myerr)
            return myerr
        }
    }
    
    if s.cfg.UseJWT {
        mylog.PrintfDebugMsg("JWT is on. Check JSON web token: reqID", reqID)
        
        cookie, err := r.Cookie("token")
        if err != nil {
            myerr := myerror.WithCause("8005", "JWT token does not present in Cookie. You have to authorize first.", err)
            mylog.PrintfErrorInfo(myerr)
            return myerr
        }
        
        if myerr = myjwt.CheckJWTFromCookie(cookie, s.cfg.JwtKey); myerr != nil {
            mylog.PrintfErrorInfo(myerr)
            return myerr
        }
    }
    
    mylog.PrintfDebugMsg("Reading request body: reqID", reqID)
    requestBuf, err := ioutil.ReadAll(r.Body)
    if err != nil {
        myerr = myerror.WithCause("8001", "Failed to read HTTP body: reqID", err, reqID)
        mylog.PrintfErrorInfo(myerr)
        return myerr
    }
    mylog.PrintfDebugMsg("Read request body: reqID, len(body)", reqID, len(requestBuf))
    
    mylog.PrintfDebugMsg("Calling external function handler: reqID, function", reqID, fn)
    responseBuf, header, status, myerr := fn(requestBuf, reqID)
    if myerr != nil {
        mylog.PrintfErrorInfo(myerr)
        return myerr
    }
    
    if s.cfg.UseHSTS {
        w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
    }
    
    if s.logger != nil {
        mylog.PrintfDebugMsg("Logging HTTP out response: reqID", reqID)
        _ = s.logger.LogHTTPOutResponse(s.tx, header, responseBuf, status, reqID) 
    }
    
    mylog.PrintfDebugMsg("Set HTTP response headers: reqID", reqID)
    if header != nil {
        for key, h := range header {
            w.Header().Set(key, h)
        }
    }
    
    mylog.PrintfDebugMsg("Set HTTP response status: reqID, Status", reqID, http.StatusText(status))
    w.WriteHeader(status)
    
    if responseBuf != nil && len(responseBuf) > 0 {
        mylog.PrintfDebugMsg("Writing HTTP response body: reqID, len(body)", reqID, len(responseBuf))
        respWrittenLen, err := w.Write(responseBuf)
        if err != nil {
            myerr = myerror.WithCause("8002", "Failed to write HTTP repsonse: reqID", err)
            mylog.PrintfErrorInfo(myerr)
            return myerr
        }
        mylog.PrintfDebugMsg("Written HTTP response: reqID, len(body)", reqID, respWrittenLen)
    }
    return nil
}