如何防止在AkkaHttp重定向请求中对URI进行百分比解码



我正在使用Akka Http来处理存储在GCS(谷歌云存储(中的文本资源的签名url的重定向。基本上,我在GCS中有一些文件,我想在要求时与我的API一起交付。不幸的是,该资源的路径有一个子目录,其中包含一个=字符,类似于:https://storage.googleapis.com/path_start/more_path/format=avro/still_more_path/filename.avro

谷歌存储的signUrl方法在签名的url中将这个=字符百分比编码为%3D。我已经验证了使用这个签名的url可以访问GCS中的文件(点击它开始下载(。问题是,当Akka Http根据请求重定向此签名url时,它会将其解码回等号。这会导致GCS:出现SignatureDoesNotMatch错误


<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>

我尝试生成签名的url,并在以下代码片段中重定向它:

val signedUrl = cloudStorageService.getSignedUrl(bucketId, filePath)
logger.info(s"signedUrl: $signedUrl")
redirect(signedUrl, TemporaryRedirect)

我的cloudStorageService.getSignedUrl方法在这里单独定义:

def getSignedUrl(bucket: String, path: String, expiration: Option[Int] = None): String = {
val maxLifetime = 60 * 60 * 24 * 7
val lifetime = expiration.getOrElse(maxLifetime)
val cappedExpiration = Math.min(lifetime, maxLifetime)

val blobInfo = BlobInfo.newBuilder(BlobId.of(bucket, path)).build
storage.signUrl(blobInfo, cappedExpiration, TimeUnit.SECONDS, Storage.SignUrlOption.withV4Signature).toString
}

第一个片段中的日志记录语句显示,签名的URL是用%3D生成的,但重定向的URL已被百分比解码回=,从而导致SignatureDoesNotMatch错误。

这种行为是AkkaHttp的标准行为。根据Uri类的文档,所有成员都表示解码元素的百分比。可以选择使用原始查询字符串构造uri,但在我的情况下,编码字符发生在uri路径本身,而不是查询中。

所以我的问题是,有没有任何方法可以让AkkaHttp重定向这个签名的url,而不需要对路径进行百分比解码?或者我错了,还有另一种方法可以解决这个问题?

您可能希望"手动"创建重定向响应,而不是使用"重定向"指令。指令执行是:

HttpResponse(
status = redirectionType,
headers = headers.Location(uri) :: Nil,
entity = redirectionType.htmlTemplate match {
case ""       => HttpEntity.Empty
case template => HttpEntity(ContentTypes.`text/html(UTF-8)`, template format uri)
}

您可以引入自己的RawHeader来构建Location头,而不是headers.Location(uri)。在您的场景中,您可能会将实体留空。

(相关文件:https://doc.akka.io//docs/akka-http/current/routing-dsl/directives/route-directives/redirect.html)