zoukankan      html  css  js  c++  java
  • Xitrum学习笔记12

    一、Request

    参数种类

    1. 文本参数:名为textParams,类型为 scala.collection.mutable.Map[Sting, Seq[String]]

    1)queryParams:URL中 ? 后面的参数,例:http://example.com/blah?x=1&y=2

    2)bodyTextParams:在POST请求体里的参数

    3)pathParams:嵌入到URL的参数,例:GET("articles/:id/:title")

    从1)到3),同名参数会被后面的覆盖

    2. 上传文件参数(二进制):名为bodyFileParams,类型为scala.collection.mutable.Map[String, Seq[FileUpload]]

    访问参数

    在Action中可以直接访问以上参数,或使用访问方法

    1. 访问 textParams:

    • param("x"): 返回 String, 如果x不存在则抛出异常
    • paramo("x"): 返回 Option[String]
    • params("x"): 返回 Seq[String], 如果x不存在返回Seq.empty

    可以使用param[Int]("x"),params[Int]("x")等等方法把文本参数转化为Int, Long, Fload, Double等其他类型。

    要把文本参数转化为更多的类型,要重写convertTextParam方法

    2. 对于upload file,param[FileUpload]("x"), params[FileUpload]("x") etc.

    "at"

    在处理请求的过程中可以使用 at 传递数据。at 的类型是 scala.collection.mutable.HashMap[String, Any]。这里的 at 是Rails的@的克隆。

    示例代码:

    Articles.scala

    @GET("articles/:id")
    class ArticlesShow extends AppAction {
      def execute() {
        val (title, body) = ... // Get from DB
        at("title") = title
        respondInlineView(body)
      }
    }

    AppAction.scala

    import xitrum.Action
    import xitrum.view.DocType
    trait AppAction extends Action {
      override def layout = DocType.html5(
        <html>
        <head>
          {antiCsrfMeta}
          {xitrumCss}
          {jsDefaults}
          <title>{if (at.isDefinedAt("title")) "My Site - " + at("title") else "My Site"}</title>
        </head>
        <body>
          {renderedView}
          {jsForView}
        </body>
        </html>
      )
    }

    atJson

    atJson方法可以自动把 at("key")的值转化成JSON,它在从Scala传递model到JavaScript时很有用

    atJson("key") 和 xitrum.util.SeriDeseri.toJson(at("key")) 是等价的

    Action.scala

    case class User(login: String, name: String)
    ...
      def execute() {
        at("user") = User("admin", "Admin")
        respondView()
      }
    ...

    Action.ssp

    <script type="text/javascript">
    var user = ${atJson("user")};
    alert(user.login);
    alert(user.name);
    </script>

    好像jade中,没有使用atJson好的方式,下列代码执行后,view无法获取user的login和name属性值

    - var user = atJson("user")
    h1 #{user}

    #{user}会显示{&quot;login&quot;:&quot;admin&quot;,&quot;name&quot;:&quot;Admin&quot;}

    RequestVar

    以上at的用法不是类型安全的,因为你可以把任意类型的值放到HashMap中。

    为了类型安全,应该使用RequestVar包装at

    RVar.scala

    import xitrum.RequestVar
    object RVar {
      object title extends RequestVar[String]
    }

    Articles.scala

    @GET("articles/:id")
    class ArticlesShow extends AppAction {
      def execute() {
        val (title, body) = ... // Get from DB
        RVar.title.set(title)
        respondInlineView(body)
      }
    }

    AppAction.scala

    import xitrum.Action
    import xitrum.view.DocType
    trait AppAction extends Action {
      override def layout = DocType.html5(
      <html>
        <head>
          {antiCsrfMeta}
          {xitrumCss}
          {jsDefaults}
          <title>{if (RVar.title.isDefined) "My Site - " + RVar.title.get else "My Site"}</title>
        </head>
        <body>
          {renderedView}
          {jsForView}
        </body>
      </html>
      )
    }

    二、Cookie

    在Action中,使用requestCookies(Map[String, String]类型)获取从浏览器发出的cookie

    requestCookies.get("myCookie") match {
      case None => ...
      case Some(string) => ...
    }

    向浏览器发送cookie,要创建一个DefaultCookie实例并把它添加到 responseCookies(一个包含Cookie的ArrayBuffer)

    val cookie = new DefaultCookie("name", "value")
    cookie.setHttpOnly(true) // true: JavaScript cannot access this cookie
    responseCookies.append(cookie)

    如果没有调用cookie.setPath(cookiePath)设置cookie的路径,它的路径会被设置成站点的根路径(xitrum.Config.withBaseUrl("/"))。

    这样可以避免cookie重复(为什么?)

    要删除一个浏览器发送的cookie, 发送一个同名cookie并把它的过期时间(max age)设置成0,浏览器会立刻让这个cookie过期。

    要浏览器在关闭时删除cookie,把cookie的过期时间设置成 Long.MinValue

    cookie.setMaxAge(Long.MinValue)

    IE不支持"max-age",但是Netty会对此进行检测,对浏览器适当地输出"max-age"或"expires"。

    浏览器不会把cookie属性发回给服务器,它只发送cookie的 名-值 对。

    如果想通过给cookie值签名的方式以防止被篡改,使用

    xitrum.util.SeriDeseri.toSecureUrlSafeBase64 和 xitrum.util.SeriDeseri.fromSecureUrlSafeBase

    具体内容参照 Xitrum学习笔记23。

    Cookie中被允许的字符

    不能再cookie中使用任意的字符。例如,如果要使用UTF-8字符,需要对他们进行编码。可以使用xitrum.utill.UrlSafeBase64或xitrum.util.SeriDeseri。

    写cookie的例子:

    import io.netty.util.CharsetUtil
    import xitrum.util.UrlSafeBase64
    val value = """{"identity":"example@gmail.com","first_name":"Alexander"}"""
    val encoded = UrlSafeBase64.noPaddingEncode(value.getBytes(CharsetUtil.UTF_8))
    val cookie = new DefaultCookie("profile", encoded)
    responseCookies.append(cookie)

    读cookie的例子:

    requestCookies.get("profile").foreach { encoded =>
      UrlSafeBase64.autoPaddingDecode(encoded).foreach { bytes =>
        val value = new String(bytes, CharsetUtil.UTF_8)
        println("profile: " + value)
      }
    }

    三、Session

    Session的存储、恢复、加密由Xitrum自动完成,无需我们操心。

    在Action中,可以使用session,它是scala.collection.mutable.Map[String,Any]的一个实例。在session中的东西必须是可序列化的。

    例如,要标记用户已登录,可以把他的用户名放入session

    session("userId") = userId

    要检查用户是否还处于登录状态中,只要检查他的session中是否还有用户名

    if (session.isDefinedAt("userId")) println("This user has logged in")

    在每次访问时,存储用户ID并且从数据库中获取用户信息是一个好的做法。

    That way changes to the
    user are updated on each access (including changes to user roles/authorizations).  ???

    session.clear()

    为了防止session固定攻击,在登录Action和登出Action都调用session.clear()

    @GET("login")
    class LoginAction extends Action {
      def execute() {
        ...
        session.clear() // Reset first before doing anything else with the session
        session("userId") = userId
      }
    }

    SessionVar

    SessionVar, 和RequestVar一样, 是一种使session更加类型安全的一种方式。

    例如,要在用户登录后保存username到session中

    声明session变量

    import xitrum.SessionVar
    object SVar {
      object username extends SessionVar[String]
    }

    登录成功后

    SVar.username.set(username)

    显示用户名

    if (SVar.username.isDefined)
      <em>{SVar.username.get}</em>
    else
      <a href={url[LoginAction]}>Login</a>

    删除session变量:SVar.username.remove()

    重置整个session:session.clear()

    保存session

    Xitrum提供3中session存储方式。在 config/xitrum.conf中,可以对session存储方式进行配置:

    CookieSessionStore:在客户端保存session

    # Store sessions on client side
    store = xitrum.scope.session.CookieSessionStore

    LruSessionStore:在服务端内存保存session

    # Simple in-memory server side session store
    store {
      "xitrum.local.LruSessionStore" {
        maxElems = 10000
      }
    }

    如果在一个集群中运行多个服务器,可以使用Hazelcast保存集群可感知的session。

    参照 https://github.com/xitrum-framework/xitrum-hazelcast

    当使用CookieSessionStore或Hazelcast时,session数据必须是可序列化的。如果必须存储非序列化的内容,要使用LruSessionStore。

    如果使用了LruSessionStore,还想运行有多个服务器的集群时,必须使用支持粘性会话的负载均衡器(load balancer that supports sticky sessions这是什么东东??)

    正常情况下,这三种默认的会话存储方式足够了。如果要实现自定义的session存储,需要继承SessionStore或ServerSessionStore并实现他们的抽象方法。

    配置Session存储方式有两种形式:

    store = my.session.StoreClassName
    ##或者
    store {
      "my.session.StoreClassName" {
        option1 = value1
        option2 = value2
      }
    }

    能把session存在客户端cookie的就尽量存在客户端(内容必须可序列化且小于4KB),因为这样更可扩展。

    只有必须把session存在server端(内存或DB)时才这样存储。

    客户端存储Session和服务器端存储session的对比

    有2种存储session的方式:

    1. 只在客户端存储

    • Session数据被存储在客户端中URL编码的cookie里
    • 服务器端不需要存储
    • 当请求到来时,服务器端从cookie里解码出session数据

    2. 客户端和服务器端都存储

    • 一个Session有两个部分:session ID 和 session数据
    • 服务器保持session存储,类似于包含 ID->数据 的检索表
    • SessionID也被存储在客户端中URL编码的cookie里
    • 当请求到来时,服务器端从cookie中解码出ID,再用ID去查看对应的数据
    • 类似于信用卡,信用卡里的不存有钱,只存有一个ID

    这两种存储session的方式中,客户端都要存储session的一些内容到cookie中(ID、数据)。

    object和val

    关于请求、Session、Cookie的操作,要使用object代替val

    不要这样写代码

    object RVar {
      val title = new RequestVar[String]
      val category = new RequestVar[String]
    }
    object SVar {
      val username = new SessionVar[String]
      val isAdmin = new SessionVar[Boolean]
    }

    以上代码可以通过编译,但是不能正确工作,因为变量内部使用类名去查找。使用val时,title和category就有了相同的类名“xitrum.RequestVar”。

    username和isAdmin也是如此。

  • 相关阅读:
    reids 入门
    关于字典的那些事儿.
    更新阿里源repo出的问题
    关于-编码进阶
    *** 数据类型之间的转化
    关于列表那些事:12.24
    while else 结构体(自测)
    关于切片的步长为正负值 的问题(自测)
    if 嵌套if 的先后顺序的区别 (自测)
    二: python基础数据类型(int,
  • 原文地址:https://www.cnblogs.com/sunspeedzy/p/6857732.html
Copyright © 2011-2022 走看看