Session

未匹配的标注

@adonisjs/session 包提供了对 session 的支持。该软件包预先配置了 web 入门模板。安装和配置它也相对简单。

// 安装
npm i @adonisjs/session
// 配置
node ace configure @adonisjs/session

# 创建:config/session.ts
# 更新:.env {"SESSION_DRIVER = cookie"}
# 更新: .adonisrc.json {providers += "@adonisjs/session"}
// 验证环境变量
/**
 * 确保将以下验证规则添加到
 * `env.ts` 文件来验证环境变量。
 */
export default Env.rules({
  // ...现有规则
  SESSION_DRIVER: Env.schema.string(),
})
  • 支持多个驱动程序。 CookiesFileRedis
  • 允许以只读模式实例化 session 存储(在 websocket 请求期间很有帮助)。
  • 支持 session 闪存消息
  • 在 npm 上查看
  • 在 GitHub 上查看

session 配置

你可以通过调整 config/session.ts 文件来配置 session 的行为。以下是默认配置文件。

import { sessionConfig } from '@adonisjs/session/build/config'

export default sessionConfig({
  enabled: true,
  driver: Env.get('SESSION_DRIVER'),
  cookieName: 'adonis-session',
  clearWithBrowser: false,
  age: '2h',
  cookie: {}, // 查看 cookie 驱动程序
  file: {}, // 查看文件驱动
  redisConnection: 'local', // 查看 redis 驱动
})
  • 启用 用作打开/关闭整个应用程序的 session 开关。
  • driver 属性定义用于存储 session 数据的驱动程序。
  • cookieName 是保存会话 id 的 cookie 的名称。可以随意将名称更改为你喜欢的任何名称。
  • clearWithBrowser 属性与 true 值创建一个临时 cookie。退出浏览器时会删除临时 cookie。
  • age 属性控制会话的生命周期。

Session 驱动

session 包允许你选择一种可用的驱动来保存 session 数据。

你可以在 config/session.ts 文件中配置驱动程序。driver 属性依赖于 SESSION_DRIVER 环境变量。

// config/session.ts 文件
{
  driver: Env.get('SESSION_DRIVER'),
}

Cookie 驱动

cookie 驱动使用 HTTP cookie 来存储会话数据。会话数据在 cookie 中加密,因此你不必担心泄露敏感信息。

即时你的应用配置了负载均衡,cookie 驱动也能很好地工作,因为服务器上没有存储任何信息。

你可以在 config/session.ts 文件中调整 cookie 驱动的设置。

{
  /*
  |---------------------------------------------------------------
  | Cookies 设置
  |---------------------------------------------------------------
  |
  | cookie 设置用于设置 session id 的 cookie
  | 并且驱动将使用相同的值。
  |
  */
  cookie: {
    path: '/',
    httpOnly: true,
    sameSite: false,
  },
}

文件驱动

file 驱动程序将 session 数据存储在服务器文件系统上。你可以通过更新 config/session.ts 文件中的 file.location 属性的值来配置存储位置。

注:使用 file 驱动在配置了负载均衡的服务器运行时,需要你在负载均衡上启用粘性会话。

{
  file: {
    location: Application.tmp('sessions'),
  },
}

Redis

redis 驱动是将 session 数据保留在服务器上,并需要配置负载均衡的最佳选择。

注:redis 驱动依赖于 @adonisjs/redis 包,首先应对其进行配置。

redis 驱动程序的配置引用了config/redis.ts文件中预定义的 redis 连接之一。

// 标题: config/session.ts
{
  driver: 'redis',
  // highlight-start
  redisConnection: 'local',
  // highlight-end
}

接下来,在config/redis.ts文件中定义一个名为local的连接。

// 标题: config/redis.ts
{
  connections: {
    // 高亮开始
    local: {
      host: Env.get('REDIS_HOST'),
      port: Env.get('REDIS_PORT'),
      password: Env.get('REDIS_PASSWORD', ''),
      db: 0,
    }
    // 高亮结束
  }
}

读/写 session 值

你可以使用ctx.session属性与会话进行交互。

Route.get('/', async ({ session }) => {
  // 读取值
  const cartTotal = session.get('cart_total')

  // 写入值
  session.put('cart_total', cartTotal + 10)
})

Edge 模板中还提供了会话的只读版本。你可以使用session全局帮助程序访问它。

<p> Cart total: {{ session.get('cart_total', 0) }} </p>

以下是工作会话的可用方法列表。

get

从 session 存储中读取给定键的值。或者,你可以定义一个默认值以在实际值为undefinednull时返回。

注:模板中也可以使用以下方法。

session.get('cart_total')
session.get('cart_total', 0)

put

将键值对写入会话存储。该值应该是 cookie 支持的数据类型 之一。

session.put('cart_total', 1900)

all

从会话存储中读取所有内容。将始终是键值对的对象。

注:模板中也可以使用以下方法。

console.log(session.all())

Redis驱动的配置使用了config/redis.ts文件中的一个预定义的redis连接。

// title: config/session.ts
{
  driver: 'redis',
  // highlight-start
  redisConnection: 'local',
  // highlight-end
}

接下来,在config/redis.ts文件中定义一个名为local的连接。

// title: config/redis.ts
{
  connections: {
    // highlight-start
    local: {
      host: Env.get('REDIS_HOST'),
      port: Env.get('REDIS_PORT'),
      password: Env.get('REDIS_PASSWORD', ''),
      db: 0,
    }
    // highlight-end
  }
}

读/写 session 值

你可以通过使用ctx.session属性与session会话进行交互。

Route.get('/', async ({ session }) => {
  // 获取session值
  const cartTotal = session.get('cart_total')

  // 设置session值
  session.put('cart_total', cartTotal + 10)
})

会话的只读版本在Edge模板中也是可用的。你可以使用session全局帮助器来访问它。

<p> Cart total: {{ session.get('cart_total', 0) }} </p>

以下是可用的session方法列表

get

从会话存储中读取一个给定键的值。你可以选择定义一个默认值,当实际值为 undefinednull时返回。

注:以下方法在模板内部也是可用的。

session.get('cart_total')
session.get('cart_total', 0)

put

写一个键值对到会话存储。该值应该是 cookie支持的数据类型 之一。

session.put('cart_total', 1900)

all

从会话存储中读取所有内容。将返回一个键值对的对象。

注:以下方法在模板内部也是可用的。

console.log(session.all())

forget

从 session 存储中删除给定键的值。

// Remove
session.forget('cart_total')

session.get('cart_total') // undefined

increment

增加给定键的值。确保原始值始终是数字。对非数字值调用increment将导致异常。

session.increment('page_views')

decrement

减少给定键的值。确保原始值始终是数字。对非数字值调用decrement将导致异常。

session.decrement('score')

clear

将 session 存储清除为空状态。

session.clear()

Session id 生命周期

AdonisJS 创建一个空的会话存储,并在第一个 HTTP 请求上将其分配给一个唯一的会话 id,即使请求/响应生命周期不与会话交互。

每个后续请求都会更新会话 id cookie 的maxAge属性,以确保它不会过期。此外,会话驱动程序会收到有关更改(如果有)的通知,以更新和保留更改。

sessionId

你可以使用sessionId属性访问会话 ID 的值。

console.log(session.sessionId)

initiated

查找会话存储是否已启动。在 HTTP 请求期间,这将始终为true

if (!session.initiated) {
  await session.initiate(false)
}

fresh

查看当前 HTTP 请求期间是否生成了会话 ID。首次生成会话 id 或调用session.regenerate方法时,该值为 true

if (!session.fresh) {
  session.regenerate()
}

regenerate

重新生成会话 id 并进行现有会话数据追加。 auth 包使用这种方法来防止会话劫持攻击

session.regenerate()

会话闪存消息

闪存消息存储在会话存储中,仅可用于下一个 HTTP 请求。你可以使用它们在 HTTP 请求之间传递消息。例如:

Route.get('/', async ({ session, response }) => {
  session.flash('message', 'Hello world')
  response.redirect('/see-message')
})

Route.get('/see-message', async ({ session }) => {
  return session.flashMessages.get('message')
})

查看视频演示

flash

session.flash 方法将键值对添加到闪存消息中。

session.flash('errors', {
  title: 'Post title is required',
  description: 'Post description is required',
})

你也可以将对象直接传递给 flash 方法。

session.flash({
  errors: {
    title: 'Post title is required',
    description: 'Post description is required',
  },
})

flashAll

flashAll 方法将请求正文添加到闪存消息中,这允许你在模板中获取表单数据并在验证失败重定向后预填充用户输入。

session.flashAll()

flashOnly

session.flashOnly 方法类似于 flashAll 方法,但它允许挑选字段。

session.flashOnly(['title', 'description'])

flashExcept

session.flashExcept 方法与 flashOnly 方法不同,允许忽略字段。

session.flashExcept(['_csrf', 'submit'])

reflash

session.reflash 方法会刷新上一个请求中的数据。

session.reflash()

reflashOnly

session.reflashOnly 方法仅刷新选定的键。

session.reflashOnly(['errors'])

reflashExcept

session.reflashExcept 方法刷新所有数据,除了选定的键。

session.reflashExcept(['success', 'username', 'password'])

访问闪存消息

你可以使用 Edge 模板中的 session.flashMessages 属性或 flashMessages 帮助程序访问上一个请求设置的闪存消息。

// 文件名: 内部模板
{{-- Get value for a given key --}}
{{ flashMessages.get('errors.title') }}

{{-- With optional default value --}}
{{ flashMessages.get('title', '') }}

{{-- Find if a key exists --}}
{{ flashMessages.has('errors.title') }}

{{-- Get all --}}
{{ flashMessages.all() }}

{{-- Find if store is empty --}}
{{ flashMessages.isEmpty }}
Route.get('/', async ({ session }) => {
  // 获取给定键的值
  session.flashMessages.get('errors.title')

  // 带有可选的默认值
  session.flashMessages.get('title', '')

  // 判断键是否存在
  session.flashMessages.has('errors.title')

  // 获取所有
  session.flashMessages.all()

  // 判断 store 是否为空
  session.flashMessages.isEmpty
})

其他方法/属性

以下是 Session 类上其他可用方法和属性的列表。

初始化

session.initiate 方法为当前 HTTP 请求启用会话存储。或者,你可以在 readonly 模式下启用存储。

注意:
此方法由 AdonisJS 自动调用,不必自己手动调用它。

await session.initiate(false)

// 只读存储
await session.initiate(true)

fresh

查看当前 HTTP 请求期间是否生成了 session id。首次生成 session id 或调用 session.regenerate 方法时,该值为 true

if (!session.fresh) {
  session.regenerate()
}

readonly

查找存储是否已在 readonly 模式下启动。

注:在 HTTP 请求期间,存储处于 从不 只读模式。此标志是为将来保留的,用于 WebSocket 连接的只读会话。

if (!session.readonly) {
  session.put('key', 'value')
}

commit

commit 方法保持会话驱动程序的更改并更新包含 session id 的 cookie 的 maxAgecommit 方法由 AdonisJS 自动调用,你不必自己调用它。

await session.commit()

创建自定义 session 驱动

session 包公开了用于添加自定义会话驱动程序的 API。每个 session 驱动都必须遵守 SessionDriverContract

interface SessionDriverContract {
  read(
    sessionId: string
  ): Promise<Record<string, any> | null> | Record<string, any> | null

  write(sessionId: string, values: Record<string, any>): Promise<void> | void

  destroy(sessionId: string): Promise<void> | void

  touch(sessionId: string): Promise<void> | void
}

read

read 方法接收 sessionId 并且必须返回会话数据或 null。返回值应是一个类似于传递给 write 方法的对象。


write

write 方法接收要存储的 sessionIdvalues 对象。你可以轻易地将值对象转换为您想要的任何其他数据类型。例如,redis 驱动器使用消息生成器将对象转换为字符串。


destroy

destroy 方法会从存储中删除会话 ID 及其相关数据。


touch

touch 方法能重置过期时间。此方法仅适用于具有内置到期的驱动器。例如,redis 驱动程序会更新 redis 键的 ttl 属性。


从外到内的扩展

任何你扩展框架核心的时候,最好假设你无权访问应用程序代码及其依赖项。换句话说,像编写第三方包一样编写扩展,并使用依赖注入来依赖其他依赖。

来演示一下,从创建一个会话驱动程序来将会话存储在内存中,并建一些文件和文件夹开始。

mkdir providers/SessionDriver
touch providers/SessionDriver/index.ts

目录结构如下所示:

providers
└── SessionDriver
    └── index.ts

打开 SessionDriver/index.ts 文件并将以下内容粘贴到其中。

// 文件名: providers/SessionDriver/index.ts
import { SessionDriverContract } from '@ioc:Adonis/Addons/Session'

const SESSIONS: Map<string, Record<string, any>> = new Map()

export class MemoryDriver implements SessionDriverContract {
  public async read(sessionId: string) {
    return SESSIONS.get(sessionId) || null
  }

  public async write(sessionId: string, values: Record<string, any>) {
    SESSIONS.set(sessionId, values)
  }

  public async destroy(sessionId: string) {
    SESSIONS.delete(sessionId)
  }

  public async touch() {}
}

最后,打开 providers/AppProvider.ts 文件并在 boot 方法中添加自定义驱动器。

import { ApplicationContract } from '@ioc:Adonis/Core/Application'

export default class AppProvider {
  public static needsApplication = true

  constructor(protected app: ApplicationContract) {}

  public async boot() {
    const { MemoryDriver } = await import('./SessionDriver')
    const Session = this.app.container.use('Adonis/Addons/Session')

    Session.extend('memory', () => {
      return new MemoryDriver()
    })
  }
}

完成了!memory 驱动程序现在已准备就绪。只需更新 .env 文件中的 SESSION_DRIVER 属性,就可以继续你的后续操作了。

驱动器生命周期

每个 HTTP 请求会创建一个新的驱动器实例,你可以从 Session.extend 方法回调参数访问 HTTP 上下文。例如:

Session.get('memory', (sessionManager, config, ctx) => {
  // 如果你的驱动器需要上下文
  return new Driver(ctx)
})

注入依赖

如前所述,扩展不应直接依赖应用程序依赖项,而是利用依赖项注入。

例如,如果你的驱动程序需要访问加密模块,它应该作为构造函数参数而不是直接进行导入。

/**
 * 以下是一旦TypeScript 被编译成 JavaScript,仅类型会导
 * 入,其他会被删除。
 * 因此,理想情况下,你不依赖任何顶级
 * 导入,仅使用接口进行类型提示。
 */
import type { EncryptionContract } from '@ioc:Adonis/Core/Encryption'

export class MemoryDriver {
  constructor(private encryption: EncryptionContract) {}

  public async write(sessionId: string, values: { [key: string]: any }) {
    this.encryption.encrypt(JSON.stringify(values))
  }
}

最后,你可以在 Session.extend 调用期间注入加密模块。

Session.extend('memory', ({ app }) => {
  return new MemoryDriver(app.container.use('Adonis/Core/Encryption'))
})

驱动配置

你还必须通过构造函数注入驱动程序的配置。session.extend 方法为你提供保存在 config/session.ts 文件中的配置。

驱动程序的配置存储在与驱动程序名称匹配的属性中。例如:

// 文件名: config/session.ts
{
  // 以下对象用于内存驱动器
  memory: {}
}
Session.extend('memory', (app, config) => {
  /**
   * 配置是“内存”属性的值
   */
  return new MemoryDriver(config)
})

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/adonisjs/5.x/ht...

译文地址:https://learnku.com/docs/adonisjs/5.x/ht...

上一篇 下一篇
贡献者:4
讨论数量: 0
发起讨论 只看当前版本


暂无话题~