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>

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":"[email protected]","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也是如此。

时间: 2024-10-09 12:51:32

Xitrum学习笔记12 - 范围的相关文章

python基础教程_学习笔记12:充电时刻——模块

充电时刻--模块 python的标准安装包括一组模块,称为标准库. 模块 >>> import math >>> math.sin(0) 0.0 模块是程序 任何python程序都可以作为模块导入. $ cat hello.py #!/usr/bin/python print "Hello,signjing!" $ ./hello.py Hello,signjing! 假设将python程序保存在/home/ggz2/magiccube/mysh/p

python 学习笔记 12 -- 写一个脚本获取城市天气信息

最近在玩树莓派,前面写过一篇在树莓派上使用1602液晶显示屏,那么能够显示后最重要的就是显示什么的问题了.最容易想到的就是显示时间啊,CPU利用率啊,IP地址之类的.那么我觉得呢,如果能够显示当前时间.温度也是甚好的,作为一个桌面小时钟还是很精致的. 1. 目前有哪些工具 目前比较好用的应该是 weather-util, 之前我获取天气信息一般都是通过它. 使用起来也很简单: (1) Debian/Ubuntu 用户使用 sudo apt-get install weather-util 安装

Swift学习笔记(12)--数组和字典的复制

Swift中,数组Array和字典Dictionary是用结构来实现的,但是数组与字典和其它结构在进行赋值或者作为参数传递给函数的时候有一些不同. 并且数组和字典的这些操作,又与Foundation中的NSArray和NSDictionary不同,它们是用类来实现的. 注意:下面的小节将会介绍数组,字典,字符串等的复制操作.这些复制操作看起来都已经发生,但是Swift只会在确实需要复制的时候才会完整复制,从而达到最优的性能. 字典的赋值和复制操作 每次将一个字典Dictionary类型赋值给一个

[原创]java WEB学习笔记12:一个简单的serlet连接数据库实验

本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 ---------------------------------

《JavaScript高级程序设计》学习笔记12篇

写在前面: 这12篇博文不是给人看的,而是用来查的,忘记了什么基础知识,点开页面Ctrl + F关键字就好了 P.S.如果在对应分类里没有找到,麻烦告诉我,以便尽快添上.当然,我也会时不时地添点遗漏的东西进去 目录 JS学习笔记1_基础与常识 JS学习笔记2_面向对象 JS学习笔记3_函数表达式 JS学习笔记4_BOM JS学习笔记5_DOM JS学习笔记6_事件 JS学习笔记7_表单脚本 JS学习笔记8_错误处理 JS学习笔记9_JSON JS学习笔记10_Ajax JS学习笔记11_高级技巧

MiZ702学习笔记12&mdash;&mdash;封装一个普通的VGA IP

还记得<MiZ702学习笔记(番外篇)--纯PL VGA驱动>这篇文章中,用verilog写了一个VGA驱动.我们今天要介绍的就是将这个工程打包成一个普通的IP,目的是为后面的一篇文章做个铺垫. 打包成一个普通的IP的目的,可以直接将这个IP粘贴到Block文件中.(和用文本实例化是一个意思).应为我们调用zynq的核的时候一般是用Block的形式,为了zynq和我们的VGA模块更方便的组织起来,就需要这种IP打包方式. 为什么是强调是普通的IP,这个主意是区分带AXI接口的IP,这个在后面介

《ASP.NET MVC 4 实战》学习笔记 12:结束与总结

本篇是<ASP.NET MVC 4 实战>这本书学习笔记的最后一篇,最后还是没有坚持将全书的内容学习下来... 其实后面还讲了许多内容:AutoMapper.区域.NHibernate.测试.部署等,但是我基本都搞不懂了. 当时选这本书作教材就是因为偏向实践,可越往后越偏向于理论,很多知识点没有实例也搞不懂在讲什么. 原因有三:1.学习能力不强:2.心情有点急躁:3.不知是原文就这样还是翻译的原因,很多地方的表述很奇怪,不知道到底想表达什么. 因为没有坚持下来,心情很不美丽,不过这段时间的学习

Xitrum学习笔记03 - Action和View

Action: Xitrum 提供了3种Action:普通Action, FutureAction 和 ActorAction 它们都是Trait,而不是 Class 1. 普通Action: 当请求到来时,Action实现类直接在Netty的 IO线程上运行,不能用普通Action来执行 消耗很长时间的处理,否则Netty就不能接收新的连接或发出响应到客户端 import xitrum.Action import xitrum.annotation.GET @GET("hello")

Codeigniter入门学习笔记12—session操作

很久很久以前学习Codeigniter的笔记记录,很随意,但都是自己记录的,希望对需要的人有所帮助. 本文使用word2013编辑并发布 Postbird | There I am , in the world more exciting! Postbird personal website : http://www.ptbird.cn session CI中的session是存在cookie中的 1.session数据的存储 $this->session->set_userdata('use