Let's Go تمرینات راهنما › هدایت کاربر به درستی پس از ورود به سیستم
قبلی · فهرست · بعدی
فصل 16.5.

هدایت کاربر به درستی پس از ورود به سیستم (Redirect User Appropriately After Login)

اگر یک کاربر غیرمجاز (Unauthenticated User) سعی کند به GET /account/view دسترسی پیدا کند، به صفحه ورود (Login Page) هدایت می‌شود. سپس پس از ورود موفقیت‌آمیز (Successful Login)، به فرم (Form) GET /snippet/create هدایت می‌شود. این برای کاربر گیج‌کننده و نامناسب است، زیرا در صفحه‌ای متفاوت از جایی که در ابتدا می‌خواستند بروند، قرار می‌گیرند.

هدف شما در این تمرین این است که برنامه (Application) را به‌روزرسانی کنید تا کاربران پس از ورود به صفحه مورد نظر (Target Page) که در ابتدا می‌خواستند به آن بروند، هدایت شوند.

مرحله 1 (Step 1)

میان‌افزار (Middleware) requireAuthentication() را به‌روزرسانی کنید تا قبل از اینکه یک کاربر غیرمجاز به صفحه ورود هدایت شود، مسیر URL (URL Path) که سعی در دسترسی به آن دارند به داده‌های جلسه (Session Data) آن‌ها اضافه شود.

نمایش کد پیشنهادی

مرحله 2 (Step 2)

مدیریت‌کننده (Handler) userLogin را به‌روزرسانی کنید تا پس از ورود موفقیت‌آمیز کاربر، داده‌های جلسه کاربر را برای مسیر URL بررسی کند. اگر وجود دارد، آن را از داده‌های جلسه حذف کرده و کاربر را به آن مسیر URL هدایت کنید. در غیر این صورت، به‌طور پیش‌فرض (Default) کاربر را به /snippet/create هدایت کنید.

نمایش کد پیشنهادی

کد پیشنهادی

کد پیشنهادی برای مرحله 1

File: cmd/web/middleware.go
package main

...

func (app *application) requireAuthentication(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !app.isAuthenticated(r) {
            // Add the path that the user is trying to access to their session
            // data.
            app.sessionManager.Put(r.Context(), "redirectPathAfterLogin", r.URL.Path)
            http.Redirect(w, r, "/user/login", http.StatusSeeOther)
            return
        }

        w.Header().Add("Cache-Control", "no-store")

        next.ServeHTTP(w, r)
    })
}

...

کد پیشنهادی برای مرحله 2

File: cmd/web/handlers.go
package main

...

func (app *application) userLoginPost(w http.ResponseWriter, r *http.Request) {
    var form userLoginForm

    err := app.decodePostForm(r, &form)
    if err != nil {
        app.clientError(w, http.StatusBadRequest)
        return
    }

    form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank")
    form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address")
    form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")

    if !form.Valid() {
        data := app.newTemplateData(r)
        data.Form = form

        app.render(w, r, http.StatusUnprocessableEntity, "login.tmpl", data)
        return
    }

    id, err := app.users.Authenticate(form.Email, form.Password)
    if err != nil {
        if errors.Is(err, models.ErrInvalidCredentials) {
            form.AddNonFieldError("Email or password is incorrect")

            data := app.newTemplateData(r)
            data.Form = form

            app.render(w, r, http.StatusUnprocessableEntity, "login.tmpl", data)
        } else {
            app.serverError(w, r, err)
        }
        return
    }

    err = app.sessionManager.RenewToken(r.Context())
    if err != nil {
        app.serverError(w, r, err)
        return
    }

    app.sessionManager.Put(r.Context(), "authenticatedUserID", id)

    // Use the PopString method to retrieve and remove a value from the session
    // data in one step. If no matching key exists this will return the empty
    // string.
    path := app.sessionManager.PopString(r.Context(), "redirectPathAfterLogin")
    if path != "" {
        http.Redirect(w, r, path, http.StatusSeeOther)
        return
    }

    http.Redirect(w, r, "/snippet/create", http.StatusSeeOther)
}

...