Play Framework:如何为每个响应添加 Header
Play Framework: How to Add a Header to Every Response
在下面的 Controller
中,Authenticated
从请求 header 中提取令牌并当且仅当令牌有效时调用给定的操作(代码已简化为清楚起见):
object MyController extends Controller {
def Authenticated(action: Token => EssentialAction) = EssentialAction { requestHeader =>
val jwt = requestHeader.headers.get(HeaderNames.AUTHORIZATION) match {
case Some(header) => s"""$AuthScheme (.*)""".r.unapplySeq(header).map(_.head.trim)
case _ => requestHeader.getQueryString("auth").map(UriEncoding.decodePath(_, SC.US_ASCII.name))
}
jwt match {
case Some(t) if t.isValid =>
val token: Token = authService.token(t)
action(token)(requestHeader)
case _ => Done(Unauthorized.withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthScheme))
}
}
def getUser(userId: String) = Authenticated { token =>
Action.async { request =>
userService.find(userId).map {
case Some(user) => Ok(Json.obj("user" -> user.asJson)).withHeaders(
"token" -> authService.renew(token).asJson.toString
)
case _ => NotFound
}
}
}
}
由 authService.token(t)
编辑的令牌 return 是一个 JWT(JSON Web 令牌)并且只能使用一次...所以我需要 return每次请求后的新令牌。这个想法是将新令牌放在响应 header 中。也就是说,有没有一种方法可以将 token
header 添加到每个响应中,而不必在每个操作中调用 withHeader
?
我会使用 ActionComposition。在 Java 中它可能看起来像:
public class YourActionComposition extends Action<YourAnnotation> {
@With(YourActionComposition.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface YourAnnotation {
}
public F.Promise<Result> call(Http.Context ctx) throws Throwable {
Promise<Result> call = delegate.call(ctx);
// Add something to your headers here
return call;
}
}
只需创建一个过滤器,然后在 Global.scala 中添加 WithFilters class。
import play.api.mvc._
object Global extends WithFilters(TokenFilter) {
...
}
这是一个用于日志记录的过滤器示例,因此您可以轻松更改它以满足您的需要。
val loggingFilter = Filter { (next, rh) =>
val start = System.currentTimeMillis
def logTime(result: PlainResult): Result = {
val time = System.currentTimeMillis - start
Logger.info(s"${rh.method} ${rh.uri} took ${time}ms and returned ${result.header.status}")
result.withHeaders("Request-Time" -> time.toString)
}
next(rh) match {
case plain: PlainResult => logTime(plain)
case async: AsyncResult => async.transform(logTime)
}
}
在下面的 Controller
中,Authenticated
从请求 header 中提取令牌并当且仅当令牌有效时调用给定的操作(代码已简化为清楚起见):
object MyController extends Controller {
def Authenticated(action: Token => EssentialAction) = EssentialAction { requestHeader =>
val jwt = requestHeader.headers.get(HeaderNames.AUTHORIZATION) match {
case Some(header) => s"""$AuthScheme (.*)""".r.unapplySeq(header).map(_.head.trim)
case _ => requestHeader.getQueryString("auth").map(UriEncoding.decodePath(_, SC.US_ASCII.name))
}
jwt match {
case Some(t) if t.isValid =>
val token: Token = authService.token(t)
action(token)(requestHeader)
case _ => Done(Unauthorized.withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthScheme))
}
}
def getUser(userId: String) = Authenticated { token =>
Action.async { request =>
userService.find(userId).map {
case Some(user) => Ok(Json.obj("user" -> user.asJson)).withHeaders(
"token" -> authService.renew(token).asJson.toString
)
case _ => NotFound
}
}
}
}
由 authService.token(t)
编辑的令牌 return 是一个 JWT(JSON Web 令牌)并且只能使用一次...所以我需要 return每次请求后的新令牌。这个想法是将新令牌放在响应 header 中。也就是说,有没有一种方法可以将 token
header 添加到每个响应中,而不必在每个操作中调用 withHeader
?
我会使用 ActionComposition。在 Java 中它可能看起来像:
public class YourActionComposition extends Action<YourAnnotation> {
@With(YourActionComposition.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface YourAnnotation {
}
public F.Promise<Result> call(Http.Context ctx) throws Throwable {
Promise<Result> call = delegate.call(ctx);
// Add something to your headers here
return call;
}
}
只需创建一个过滤器,然后在 Global.scala 中添加 WithFilters class。
import play.api.mvc._
object Global extends WithFilters(TokenFilter) {
...
}
这是一个用于日志记录的过滤器示例,因此您可以轻松更改它以满足您的需要。
val loggingFilter = Filter { (next, rh) =>
val start = System.currentTimeMillis
def logTime(result: PlainResult): Result = {
val time = System.currentTimeMillis - start
Logger.info(s"${rh.method} ${rh.uri} took ${time}ms and returned ${result.header.status}")
result.withHeaders("Request-Time" -> time.toString)
}
next(rh) match {
case plain: PlainResult => logTime(plain)
case async: AsyncResult => async.transform(logTime)
}
}