1

在 Web 编程中,表单提交的常见模式是:

  • 如果表单有效,则重定向到另一个页面(Post/Redirect/Get)。
  • 如果表单无效,请重新显示表单(这次可能包括错误消息)。

我如何在 Ocsigen 中表达这一点?

我一直在阅读文档,但无法弄清楚如何完成这个简单(而且非常常见)的任务。

示例用例:

假设我有一个登录表单,可以防止未经授权访问管理面板(该页面只能由管理员访问)。如果用户在登录表单中提供了正确的凭据,则应将用户重定向到管理面板。如果凭据不正确,则应将用户重定向回登录表单。到目前为止,我已经成功实现了这个功能。

但是,在实现以下内容时我不知所措:如果用户已经登录并尝试访问登录表单,则应将用户重定向到管理面板。此外,如果用户未登录并尝试访问管理面板,则应将用户重定向到登录表单。

下面是我卡住的地方的代码:

(* Secret login credentials. *)
let username = "admin"
let password = "123456"

(* Create Eliom services. *)
let login_form_service = Eliom_service.create
    ~path:(Eliom_service.Path ["login"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    ()

let login_service = Eliom_service.create_attached_post
    ~fallback:login_form_service
    ~post_params:Eliom_parameter.(string "user" ** string "pass")
    ()

let admin_panel_service = Eliom_service.create
    ~path:(Eliom_service.Path ["admin-panel"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    ()

let session_username : string option Eliom_reference.eref =
    Eliom_reference.eref ~scope:Eliom_common.default_session_scope None

(* Register Eliom services. *)
let () = Eliom_content.Html.D.(

    Eliom_registration.Html.register
        ~service:login_form_service
        (fun () () -> Lwt.return
            (Eliom_tools.D.html
                ~title:"Login"
                (body [h1 [pcdata "Login"];
                       Form.post_form
                           ~service:login_service
                           (fun (user, pass) ->
                               [fieldset
                                   [label [pcdata "Username: "];
                                   Form.input ~input_type:`Text
                                              ~name:user
                                              Form.string;
                                   br ();
                                   label [pcdata "Password: "];
                                   Form.input ~input_type:`Password
                                              ~name:pass
                                              Form.string;
                                   br ();
                                   Form.input ~input_type:`Submit
                                              ~value:"Login"
                                              Form.string
                                ]]) ()])));

    Eliom_registration.Redirection.register
        ~service:login_service
        (fun () (user, pass) ->
            if user = username && pass = password then (
                Eliom_reference.set session_username (Some username);
                Lwt.return (Eliom_registration.Redirection admin_panel_service))
            else
                Lwt.return (Eliom_registration.Redirection login_form_service));

    Eliom_registration.Html.register
        ~service:admin_panel_service
        (fun () () ->
            (* Admin panel html here ... *))
);

解决这个问题的正确方法是什么?

谢谢你。

4

1 回答 1

1

我不知道您是否找到了解决方案,因为您的问题现在有点老了,但这是我发现的:

我使用了https://ocsigen.org/eliom/6.3/manual/server-outputs#redirections中的“注册决定他们想要发送的内容的服务”

这个想法是使用 Eliom_registration.Any 注册您的服务,这允许附加到服务的处理程序通过使用适当的发送函数动态决定要提供的答案类型。

(* Secret login credentials. *)
let username = "admin"
let password = "123456"

(* Create Eliom services. *)
let login_form_service = Eliom_service.create
  ~path:(Eliom_service.Path ["login"])
  ~meth:(Eliom_service.Get Eliom_parameter.unit)
  ()

let login_service = Eliom_service.create_attached_post
  ~fallback:login_form_service
  ~post_params:Eliom_parameter.(string "user" ** string "pass")
  ()

let admin_panel_service = Eliom_service.create
  ~path:(Eliom_service.Path ["admin-panel"])
  ~meth:(Eliom_service.Get Eliom_parameter.unit)
  ()

let session_username : string option Eliom_reference.eref =
  Eliom_reference.eref ~scope:Eliom_common.default_session_scope None

let login_panel () = Eliom_content.Html.D.(
  Eliom_tools.D.html
    ~title:"Login"
    (body [
      h1 [pcdata "Login"];
      Form.post_form
        ~service:login_service
        (fun (user, pass) ->
          [fieldset
          [label [pcdata "Username: "];
           Form.input ~input_type:`Text
                 ~name:user
                 Form.string;
           br ();
           label [pcdata "Password: "];
           Form.input ~input_type:`Password
                 ~name:pass
                 Form.string;
           br ();
           Form.input ~input_type:`Submit
                 ~value:"Login"
                 Form.string
          ]]) ()])
)

let admin_panel () = Eliom_content.Html.F.(
  Eliom_tools.F.html
    ~title:"Fake Admin Panel"
    (body [h1 [pcdata "Fake Admin Panel"];])
)


(* Some helper functions *)
let admin_credentials ~user_is_admin ~user_is_not_admin =
  let%lwt usr = Eliom_reference.get session_username in
  if usr = Some username then user_is_admin ()
  else user_is_not_admin ()

let send_redirection service =
  Eliom_registration.Redirection.send (Eliom_registration.Redirection service)

let send_page page =
  Eliom_registration.Html.send page


(* Register Eliom services. *)
let () = Eliom_content.Html.D.(

  Eliom_registration.Any.register
    ~service:login_form_service
    (fun () () ->
      admin_credentials
        ~user_is_admin:(fun () -> send_redirection admin_panel_service)
        ~user_is_not_admin:(fun () -> send_page (login_panel ()))
    );

  Eliom_registration.Redirection.register
    ~service:login_service
    (fun () (user, pass) ->
      if user = username && pass = password then (
        let%lwt _ = Eliom_reference.set session_username (Some username) in
        Lwt.return (Eliom_registration.Redirection admin_panel_service))
      else
        Lwt.return (Eliom_registration.Redirection login_form_service));

  Eliom_registration.Any.register
    ~service:admin_panel_service
    (fun () () ->
      admin_credentials
        ~user_is_admin:(fun () -> send_page (admin_panel ()))
        ~user_is_not_admin:(fun () -> send_redirection login_form_service))
)
于 2018-04-27T14:37:21.247 回答