alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (Results.scala)

This example Play Framework source code file (Results.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

api, array, collection, core, enumeratee, int, iteratee, library, map, play, play framework, redirect, responseheader, result, status, string

The Results.scala Play Framework example source code

/*
 * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
 */
package play.api.mvc

import play.api.libs.iteratee._
import play.api.http._
import play.api.http.HeaderNames._
import play.api.http.HttpProtocol._
import play.api.{ Application, Play }
import play.api.i18n.Lang

import play.core.Execution.Implicits._
import play.api.libs.concurrent.Execution.defaultContext
import play.core.utils.CaseInsensitiveOrdered
import scala.collection.immutable.TreeMap

/**
 * A simple HTTP response header, used for standard responses.
 *
 * @param status the response status, e.g. ‘200 OK’
 * @param _headers the HTTP headers
 */
final class ResponseHeader(val status: Int, _headers: Map[String, String] = Map.empty) {
  val headers: Map[String, String] = TreeMap[String, String]()(CaseInsensitiveOrdered) ++ _headers

  def copy(status: Int = status, headers: Map[String, String] = headers): ResponseHeader =
    new ResponseHeader(status, headers)

  override def toString = s"$status, $headers"
  override def hashCode = (status, headers).hashCode
  override def equals(o: Any) = o match {
    case ResponseHeader(s, h) => (s, h).equals((status, headers))
    case _ => false
  }
}
object ResponseHeader {
  def apply(status: Int, headers: Map[String, String] = Map.empty): ResponseHeader =
    new ResponseHeader(status, headers)
  def unapply(rh: ResponseHeader): Option[(Int, Map[String, String])] =
    if (rh eq null) None else Some((rh.status, rh.headers))
}

/**
 * The connection semantics for the result.
 */
object HttpConnection extends Enumeration {
  type Connection = Value

  /**
   * Prefer to keep the connection alive.
   *
   * If no `Content-Length` header is present, and no `Transfer-Encoding` header is present, then the body will be
   * buffered for a maximum of one chunk from the enumerator, in an attempt to calculate the content length.  If the
   * enumerator contains more than one chunk, then the body will be sent chunked if the client is using HTTP 1.1,
   * or the body will be sent as is, but the connection will be closed after the body is sent.
   *
   * There are cases where the connection won't be kept alive.  These are as follows:
   *
   * - The protocol the client is using is HTTP 1.0 and the client hasn't sent a `Connection: keep-alive` header.
   * - The client has sent a `Connection: close` header.
   * - There is no `Content-Length` or `Transfer-Encoding` header present, the enumerator contains more than one chunk,
   *   and the protocol the client is using is HTTP 1.0, hence chunked encoding can't be used as a fallback.
   */
  val KeepAlive = Value

  /**
   * Close the connection once the response body has been sent.
   *
   * This will take precedence to any `Connection` header specified in the request.
   *
   * No buffering of the response will be attempted.  This means if the result contains no `Content-Length` header,
   * none will be calculated.
   */
  val Close = Value
}

/**
 * A simple result, which defines the response header and a body ready to send to the client.
 *
 * @param header the response header, which contains status code and HTTP headers
 * @param body the response body
 * @param connection the connection semantics to use
 */
case class Result(header: ResponseHeader, body: Enumerator[Array[Byte]],
    connection: HttpConnection.Connection = HttpConnection.KeepAlive) {

  /**
   * Adds headers to this result.
   *
   * For example:
   * {{{
   * Ok("Hello world").withHeaders(ETAG -> "0")
   * }}}
   *
   * @param headers the headers to add to this result.
   * @return the new result
   */
  def withHeaders(headers: (String, String)*): Result = {
    copy(header = header.copy(headers = header.headers ++ headers))
  }

  /**
   * Adds cookies to this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).withCookies(Cookie("theme", "blue"))
   * }}}
   *
   * @param cookies the cookies to add to this result
   * @return the new result
   */
  def withCookies(cookies: Cookie*): Result = {
    withHeaders(SET_COOKIE -> Cookies.merge(header.headers.get(SET_COOKIE).getOrElse(""), cookies))
  }

  /**
   * Discards cookies along this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).discardingCookies("theme")
   * }}}
   *
   * @param cookies the cookies to discard along to this result
   * @return the new result
   */
  def discardingCookies(cookies: DiscardingCookie*): Result = {
    withHeaders(SET_COOKIE -> Cookies.merge(header.headers.get(SET_COOKIE).getOrElse(""), cookies.map(_.toCookie)))
  }

  /**
   * Sets a new session for this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).withSession(session + ("saidHello" -> "true"))
   * }}}
   *
   * @param session the session to set with this result
   * @return the new result
   */
  def withSession(session: Session): Result = {
    if (session.isEmpty) discardingCookies(Session.discard) else withCookies(Session.encodeAsCookie(session))
  }

  /**
   * Sets a new session for this result, discarding the existing session.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).withSession("saidHello" -> "yes")
   * }}}
   *
   * @param session the session to set with this result
   * @return the new result
   */
  def withSession(session: (String, String)*): Result = withSession(Session(session.toMap))

  /**
   * Discards the existing session for this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).withNewSession
   * }}}
   *
   * @return the new result
   */
  def withNewSession: Result = withSession(Session())

  /**
   * Adds values to the flash scope for this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).flashing(flash + ("success" -> "Done!"))
   * }}}
   *
   * @param flash the flash scope to set with this result
   * @return the new result
   */
  def flashing(flash: Flash): Result = {
    if (shouldWarnIfNotRedirect) {
      logRedirectWarning("flashing")
    }
    withCookies(Flash.encodeAsCookie(flash))
  }

  /**
   * Adds values to the flash scope for this result.
   *
   * For example:
   * {{{
   * Redirect(routes.Application.index()).flashing("success" -> "Done!")
   * }}}
   *
   * @param values the flash values to set with this result
   * @return the new result
   */
  def flashing(values: (String, String)*): Result = flashing(Flash(values.toMap))

  /**
   * Changes the result content type.
   *
   * For example:
   * {{{
   * Ok("<text>Hello world</text>").as("application/xml")
   * }}}
   *
   * @param contentType the new content type.
   * @return the new result
   */
  def as(contentType: String): Result = withHeaders(CONTENT_TYPE -> contentType)

  /**
   * @param request Current request
   * @return The session carried by this result. Reads the request’s session if this result does not modify the session.
   */
  def session(implicit request: RequestHeader): Session =
    Cookies(header.headers.get(SET_COOKIE)).get(Session.COOKIE_NAME) match {
      case Some(cookie) => Session.decodeFromCookie(Some(cookie))
      case None => request.session
    }

  /**
   * Example:
   * {{{
   *   Ok.addingToSession("foo" -> "bar").addingToSession("baz" -> "bah")
   * }}}
   * @param values (key -> value) pairs to add to this result’s session
   * @param request Current request
   * @return A copy of this result with `values` added to its session scope.
   */
  def addingToSession(values: (String, String)*)(implicit request: RequestHeader): Result =
    withSession(new Session(session.data ++ values.toMap))

  /**
   * Example:
   * {{{
   *   Ok.removingFromSession("foo")
   * }}}
   * @param keys Keys to remove from session
   * @param request Current request
   * @return A copy of this result with `keys` removed from its session scope.
   */
  def removingFromSession(keys: String*)(implicit request: RequestHeader): Result =
    withSession(new Session(session.data -- keys))

  /**
   * Sets the user's language permanently for future requests by storing it in a cookie.
   *
   * For example:
   * {{{
   * implicit val lang = Lang("fr-FR")
   * Ok(Messages("hello.world")).withLang(lang)
   * }}}
   *
   * @param lang the language to store for the user
   * @return the new result
   */
  def withLang(lang: Lang)(implicit app: Application): Result =
    withCookies(Cookie(Play.langCookieName, lang.code))

  /**
   * Clears the user's language by discarding the language cookie set by withLang
   *
   * For example:
   * {{{
   * Ok(Messages("hello.world")).clearingLang
   * }}}
   *
   * @return the new result
   */
  def clearingLang(implicit app: Application): Result =
    discardingCookies(DiscardingCookie(Play.langCookieName))

  override def toString = {
    "Result(" + header + ")"
  }

  /**
   * Returns true if the status code is not 3xx and the application is in Dev mode.
   */
  private def shouldWarnIfNotRedirect: Boolean = {
    play.api.Play.maybeApplication.exists(app =>
      (app.mode == play.api.Mode.Dev) && (header.status < 300 || header.status > 399))
  }

  /**
   * Logs a redirect warning.
   */
  private def logRedirectWarning(methodName: String) {
    val status = header.status
    play.api.Logger("play").warn(s"You are using status code '$status' with $methodName, which should only be used with a redirect status!")
  }

}

/**
 * A Codec handle the conversion of String to Byte arrays.
 *
 * @param charset The charset to be sent to the client.
 * @param encode The transformation function.
 */
case class Codec(val charset: String)(val encode: String => Array[Byte], val decode: Array[Byte] => String)

/**
 * Default Codec support.
 */
object Codec {

  /**
   * Create a Codec from an encoding already supported by the JVM.
   */
  def javaSupported(charset: String) = Codec(charset)(str => str.getBytes(charset), bytes => new String(bytes, charset))

  /**
   * Codec for UTF-8
   */
  implicit val utf_8 = javaSupported("utf-8")

  /**
   * Codec for ISO-8859-1
   */
  val iso_8859_1 = javaSupported("iso-8859-1")

}

/** Helper utilities to generate results. */
object Results extends Results {

  /** Empty result, i.e. nothing to send. */
  case class EmptyContent()

}

/** Helper utilities to generate results. */
trait Results {

  import play.api.http.Status._

  /**
   * Generates default `Result` from a content type, headers and content.
   *
   * @param status the HTTP response status, e.g ‘200 OK’
   */
  class Status(status: Int) extends Result(header = ResponseHeader(status), body = Enumerator.empty,
    connection = HttpConnection.KeepAlive) {

    /**
     * Set the result's content.
     *
     * @param content The content to send.
     */
    def apply[C](content: C)(implicit writeable: Writeable[C]): Result = {
      Result(
        ResponseHeader(status, writeable.contentType.map(ct => Map(CONTENT_TYPE -> ct)).getOrElse(Map.empty)),
        Enumerator(writeable.transform(content))
      )
    }

    /**
     * Send a file.
     *
     * @param content The file to send.
     * @param inline Use Content-Disposition inline or attachment.
     * @param fileName function to retrieve the file name (only used for Content-Disposition attachment).
     */
    def sendFile(content: java.io.File, inline: Boolean = false, fileName: java.io.File => String = _.getName, onClose: () => Unit = () => ()): Result = {
      val name = fileName(content)
      Result(
        ResponseHeader(status, Map(
          CONTENT_LENGTH -> content.length.toString,
          CONTENT_TYPE -> play.api.libs.MimeTypes.forFileName(name).getOrElse(play.api.http.ContentTypes.BINARY)
        ) ++ (if (inline) Map.empty else Map(CONTENT_DISPOSITION -> s"""attachment; filename="$name""""))),
        Enumerator.fromFile(content) &> Enumeratee.onIterateeDone(onClose)(defaultContext)
      )
    }

    /**
     * Feed the content as the response, using chunked transfer encoding.
     *
     * Chunked transfer encoding is only supported for HTTP 1.1 clients.  If the client is an HTTP 1.0 client, Play will
     * instead return a 505 error code.
     *
     * Chunked encoding allows the server to send a response where the content length is not known, or for potentially
     * infinite streams, while still allowing the connection to be kept alive and reused for the next request.
     *
     * @param content Enumerator providing the content to stream.
     */
    def chunked[C](content: Enumerator[C])(implicit writeable: Writeable[C]): Result = {
      Result(header = ResponseHeader(status,
        writeable.contentType.map(ct => Map(
          CONTENT_TYPE -> ct,
          TRANSFER_ENCODING -> CHUNKED
        )).getOrElse(Map(
          TRANSFER_ENCODING -> CHUNKED
        ))
      ),
        body = content &> writeable.toEnumeratee &> chunk,
        connection = HttpConnection.KeepAlive)
    }

    /**
     * Feed the content as the response.
     *
     * The connection will be closed after the response is sent, regardless of whether there is a content length or
     * transfer encoding defined.
     *
     * @param content Enumerator providing the content to stream.
     */
    def feed[C](content: Enumerator[C])(implicit writeable: Writeable[C]): Result = {
      Result(
        header = ResponseHeader(status, writeable.contentType.map(ct => Map(CONTENT_TYPE -> ct)).getOrElse(Map.empty)),
        body = content &> writeable.toEnumeratee,
        connection = HttpConnection.Close
      )
    }
  }

  /**
   * Implements HTTP chunked transfer encoding.
   */
  def chunk: Enumeratee[Array[Byte], Array[Byte]] = chunk(None)

  /**
   * Implements HTTP chunked transfer encoding.
   *
   * @param trailers An optional trailers iteratee.  If supplied, this will be zipped with the output iteratee, so that
   *                 it can calculate some trailing headers, which will be included with the last chunk.
   */
  def chunk(trailers: Option[Iteratee[Array[Byte], Seq[(String, String)]]] = None): Enumeratee[Array[Byte], Array[Byte]] = {

    // Enumeratee that formats each chunk.
    val formatChunks = Enumeratee.map[Array[Byte]] { data =>
      // This will be much nicer if we ever move to ByteString
      val chunkSize = Integer.toHexString(data.length).getBytes("UTF-8")
      // Length of chunk is the digits in chunk size, plus the data length, plus 2 CRLF pairs
      val chunk = new Array[Byte](chunkSize.length + data.length + 4)
      System.arraycopy(chunkSize, 0, chunk, 0, chunkSize.length)
      chunk(chunkSize.length) = '\r'
      chunk(chunkSize.length + 1) = '\n'
      System.arraycopy(data, 0, chunk, chunkSize.length + 2, data.length)
      chunk(chunk.length - 2) = '\r'
      chunk(chunk.length - 1) = '\n'
      chunk
    }

    // The actual enumeratee, which applies the formatting enumeratee maybe zipped with the trailers iteratee, and also
    // adds the last chunk.
    new Enumeratee[Array[Byte], Array[Byte]] {
      def applyOn[A](inner: Iteratee[Array[Byte], A]) = {

        val chunkedInner: Iteratee[Array[Byte], Iteratee[Array[Byte], A]] =
          // First filter out empty chunks - an empty chunk signifies end of stream in chunked transfer encoding
          Enumeratee.filterNot[Array[Byte]](_.isEmpty) ><>
            // Apply the chunked encoding
            formatChunks ><>
            // Don't feed EOF into the iteratee - so we can feed the last chunk ourselves later
            Enumeratee.passAlong &>
            // And apply the inner iteratee
            inner

        trailers match {
          case Some(trailersIteratee) => {
            // Zip the trailers iteratee with the inner iteratee
            Enumeratee.zipWith(chunkedInner, trailersIteratee) { (it, trailers) =>
              // Create last chunk
              val lastChunk = trailers.map(t => t._1 + ": " + t._2 + "\r\n").mkString("0\r\n", "", "\r\n").getBytes("UTF-8")
              Iteratee.flatten(Enumerator(lastChunk) >>> Enumerator.eof |>> it)
            }
          }
          case None => {
            chunkedInner.map { it =>
              // Feed last chunk with no trailers
              Iteratee.flatten(Enumerator("0\r\n\r\n".getBytes("UTF-8")) >>> Enumerator.eof |>> it)
            }
          }
        }
      }
    }
  }

  /**
   * Dechunks a chunked transfer encoding stream.
   *
   * Chunks may span multiple elements in the stream.
   */
  def dechunk: Enumeratee[Array[Byte], Array[Byte]] = {

    // convenience method
    def elOrEmpty(data: Array[Byte]) = {
      if (data.length == 0) Input.Empty else Input.El(data)
    }

    // Read a line. Is quite permissive, a line is anything terminated by LF, and trims the result.
    def readLine(line: List[Array[Byte]] = Nil): Iteratee[Array[Byte], String] = Cont {
      case Input.El(data) => {
        val s = data.takeWhile(_ != '\n')
        if (s.length == data.length) {
          readLine(s :: line)
        } else {
          Done(new String(Array.concat((s :: line).reverse: _*), "UTF-8").trim(), elOrEmpty(data.drop(s.length + 1)))
        }
      }
      case Input.EOF => {
        Error("EOF found while reading line", Input.Empty)
      }
      case Input.Empty => readLine(line)
    }

    // Read the data part of a chunk of the given size
    def readChunkData(size: Int, chunk: List[Array[Byte]] = Nil): Iteratee[Array[Byte], Array[Byte]] = Cont {
      case Input.El(data) => {
        if (data.length >= size) {
          Done(Array.concat((data.take(size) :: chunk).reverse: _*), elOrEmpty(data.drop(size)))
        } else {
          readChunkData(size - data.length, data :: chunk)
        }
      }
      case Input.EOF => {
        Error("EOF found while reading chunk", Input.Empty)
      }
      case Input.Empty => readChunkData(size, chunk)
    }

    // Read a chunk of the given size
    def readChunk(size: Int) = for {
      chunk <- readChunkData(size)
      // Following every chunk data is a newline - read it
      _ <- readLine()
    } yield chunk

    // Read the last chunk. Produces the trailers.
    def readLastChunk: Iteratee[Array[Byte], List[(String, String)]] = for {
      trailer <- readLine(Nil)
      trailers <- if (trailer.length > 0) readLastChunk else Done[Array[Byte], List[(String, String)]](List.empty[(String, String)])
    } yield {
      trailer.split("""\s*:\s*""", 2) match {
        case Array(key, value) => (key -> value) :: trailers
        case Array(key) => (key -> "") :: trailers
      }
    }

    // A chunk parser, produces elements that are either chunks or the last chunk trailers
    val chunkParser: Iteratee[Array[Byte], Either[Array[Byte], Seq[(String, String)]]] = for {
      size <- readLine().map { line =>
        def isHexDigit(c: Char) = Character.isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
        // Parse the size. Ignore any extensions.
        Integer.parseInt(line.takeWhile(isHexDigit), 16)
      }
      chunk <- if (size > 0) readChunk(size).map(Left.apply) else readLastChunk.map(Right.apply)
    } yield chunk

    Enumeratee.grouped(chunkParser) ><>
      Enumeratee.takeWhile[Either[Array[Byte], Seq[(String, String)]]](_.isLeft) ><>
      Enumeratee.map {
        case Left(data) => data
      }
  }

  /** Generates a ‘200 OK’ result. */
  val Ok = new Status(OK)

  /** Generates a ‘201 CREATED’ result. */
  val Created = new Status(CREATED)

  /** Generates a ‘202 ACCEPTED’ result. */
  val Accepted = new Status(ACCEPTED)

  /** Generates a ‘203 NON_AUTHORITATIVE_INFORMATION’ result. */
  val NonAuthoritativeInformation = new Status(NON_AUTHORITATIVE_INFORMATION)

  /** Generates a ‘204 NO_CONTENT’ result. */
  val NoContent = Result(header = ResponseHeader(NO_CONTENT), body = Enumerator.empty,
    connection = HttpConnection.KeepAlive)

  /** Generates a ‘205 RESET_CONTENT’ result. */
  val ResetContent = Result(header = ResponseHeader(RESET_CONTENT), body = Enumerator.empty,
    connection = HttpConnection.KeepAlive)

  /** Generates a ‘206 PARTIAL_CONTENT’ result. */
  val PartialContent = new Status(PARTIAL_CONTENT)

  /** Generates a ‘207 MULTI_STATUS’ result. */
  val MultiStatus = new Status(MULTI_STATUS)

  /**
   * Generates a ‘301 MOVED_PERMANENTLY’ simple result.
   *
   * @param url the URL to redirect to
   */
  def MovedPermanently(url: String): Result = Redirect(url, MOVED_PERMANENTLY)

  /**
   * Generates a ‘302 FOUND’ simple result.
   *
   * @param url the URL to redirect to
   */
  def Found(url: String): Result = Redirect(url, FOUND)

  /**
   * Generates a ‘303 SEE_OTHER’ simple result.
   *
   * @param url the URL to redirect to
   */
  def SeeOther(url: String): Result = Redirect(url, SEE_OTHER)

  /** Generates a ‘304 NOT_MODIFIED’ result. */
  val NotModified = Result(header = ResponseHeader(NOT_MODIFIED), body = Enumerator.empty,
    connection = HttpConnection.KeepAlive)

  /**
   * Generates a ‘307 TEMPORARY_REDIRECT’ simple result.
   *
   * @param url the URL to redirect to
   */
  def TemporaryRedirect(url: String): Result = Redirect(url, TEMPORARY_REDIRECT)

  /** Generates a ‘400 BAD_REQUEST’ result. */
  val BadRequest = new Status(BAD_REQUEST)

  /** Generates a ‘401 UNAUTHORIZED’ result. */
  val Unauthorized = new Status(UNAUTHORIZED)

  /** Generates a ‘403 FORBIDDEN’ result. */
  val Forbidden = new Status(FORBIDDEN)

  /** Generates a ‘404 NOT_FOUND’ result. */
  val NotFound = new Status(NOT_FOUND)

  /** Generates a ‘405 METHOD_NOT_ALLOWED’ result. */
  val MethodNotAllowed = new Status(METHOD_NOT_ALLOWED)

  /** Generates a ‘406 NOT_ACCEPTABLE’ result. */
  val NotAcceptable = new Status(NOT_ACCEPTABLE)

  /** Generates a ‘408 REQUEST_TIMEOUT’ result. */
  val RequestTimeout = new Status(REQUEST_TIMEOUT)

  /** Generates a ‘409 CONFLICT’ result. */
  val Conflict = new Status(CONFLICT)

  /** Generates a ‘410 GONE’ result. */
  val Gone = new Status(GONE)

  /** Generates a ‘412 PRECONDITION_FAILED’ result. */
  val PreconditionFailed = new Status(PRECONDITION_FAILED)

  /** Generates a ‘413 REQUEST_ENTITY_TOO_LARGE’ result. */
  val EntityTooLarge = new Status(REQUEST_ENTITY_TOO_LARGE)

  /** Generates a ‘414 REQUEST_URI_TOO_LONG’ result. */
  val UriTooLong = new Status(REQUEST_URI_TOO_LONG)

  /** Generates a ‘415 UNSUPPORTED_MEDIA_TYPE’ result. */
  val UnsupportedMediaType = new Status(UNSUPPORTED_MEDIA_TYPE)

  /** Generates a ‘417 EXPECTATION_FAILED’ result. */
  val ExpectationFailed = new Status(EXPECTATION_FAILED)

  /** Generates a ‘422 UNPROCESSABLE_ENTITY’ result. */
  val UnprocessableEntity = new Status(UNPROCESSABLE_ENTITY)

  /** Generates a ‘423 LOCKED’ result. */
  val Locked = new Status(LOCKED)

  /** Generates a ‘424 FAILED_DEPENDENCY’ result. */
  val FailedDependency = new Status(FAILED_DEPENDENCY)

  /** Generates a ‘429 TOO_MANY_REQUEST’ result. */
  val TooManyRequest = new Status(TOO_MANY_REQUEST)

  /** Generates a ‘500 INTERNAL_SERVER_ERROR’ result. */
  val InternalServerError = new Status(INTERNAL_SERVER_ERROR)

  /** Generates a ‘501 NOT_IMPLEMENTED’ result. */
  val NotImplemented = new Status(NOT_IMPLEMENTED)

  /** Generates a ‘502 BAD_GATEWAY’ result. */
  val BadGateway = new Status(BAD_GATEWAY)

  /** Generates a ‘503 SERVICE_UNAVAILABLE’ result. */
  val ServiceUnavailable = new Status(SERVICE_UNAVAILABLE)

  /** Generates a ‘504 GATEWAY_TIMEOUT’ result. */
  val GatewayTimeout = new Status(GATEWAY_TIMEOUT)

  /** Generates a ‘505 HTTP_VERSION_NOT_SUPPORTED’ result. */
  val HttpVersionNotSupported = new Status(HTTP_VERSION_NOT_SUPPORTED)

  /** Generates a ‘507 INSUFFICIENT_STORAGE’ result. */
  val InsufficientStorage = new Status(INSUFFICIENT_STORAGE)

  /**
   * Generates a simple result.
   *
   * @param code the status code
   */
  def Status(code: Int) = new Status(code)

  /**
   * Generates a redirect simple result.
   *
   * @param url the URL to redirect to
   * @param status HTTP status
   */
  def Redirect(url: String, status: Int): Result = Redirect(url, Map.empty, status)

  /**
   * Generates a redirect simple result.
   *
   * @param url the URL to redirect to
   * @param queryString queryString parameters to add to the queryString
   * @param status HTTP status
   */
  def Redirect(url: String, queryString: Map[String, Seq[String]] = Map.empty, status: Int = SEE_OTHER) = {
    import java.net.URLEncoder
    val fullUrl = url + Option(queryString).filterNot(_.isEmpty).map { params =>
      (if (url.contains("?")) "&" else "?") + params.toSeq.flatMap { pair =>
        pair._2.map(value => (pair._1 + "=" + URLEncoder.encode(value, "utf-8")))
      }.mkString("&")
    }.getOrElse("")
    Status(status).withHeaders(LOCATION -> fullUrl)
  }

  /**
   * Generates a redirect simple result.
   *
   * @param call Call defining the URL to redirect to, which typically comes from the reverse router
   */
  def Redirect(call: Call): Result = Redirect(call.url)

}

Other Play Framework source code examples

Here is a short list of links related to this Play Framework Results.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.