Table of contents
1.
Introduction
2.
Actions, Controllers, and Results
2.1.
Action
2.2.
Controllers are action generators
2.3.
Redirects are simple results
3.
HTTP routing
3.1.
The built-in HTTP router
3.2.
The HTTP method
3.3.
Dependency Injection
4.
Manipulating Results
4.1.
Changing the default Content-Type
4.2.
Manipulating HTTP headers
4.3.
Setting and discarding cookies
5.
Session and Flash scopes
5.1.
Working with Cookies
5.2.
Storing data in the Session
5.3.
Reading a Session value
5.4.
Discarding the whole session
6.
Body parsers
7.
Action composition
7.1.
Custom action builders
8.
Handling errors
8.1.
Handling errors in a JSON API
8.2.
Using both JSON and HTML and other content types
9.
Frequently Asked Questions
9.1.
Is Scala frontend or backend?
9.2.
What is Scala and how it works?
9.3.
What is HTTP?
10.
Conclusion
Last Updated: Mar 27, 2024

Scala developers-HTTP programming

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Scala is a  general-purpose programming language with statically typed data types that supports functional and object-oriented programming. Many of Scala's design choices are taken to address complaints about Java. It is used in Data processing, distributed computing, and web development. It powers the data engineering infrastructure of many companies. We shall now see its various features in terms of HTTP features and examples.

Scala Meme

Actions, Controllers, and Results

Action

An Action handles most of the requests received by a Play application.

play.api.mvc.Action is a (play.api.mvc.Request => play.api.mvc.Result) function which handles a request and generates a result to be sent to the client.

def echo = Action { request =>
  Ok("Received request as [" + request + "]")
}

An action returns a play.api.mvc.Result value, showing the HTTP response to send to the web client. In this example Ok constructs a 200 OK response containing a plain/text response body.

Controllers are action generators

A controller in Play is an object that shows Action values. Controllers are typically declared as classes to take advantage of Dependancy Injection.

The most effortless use case for defining an action generator is a method with no parameters that gives back an Action value:

package controllers

import javax.inject.Inject
  import play.api.mvc._
  class Application @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
    def index = Action {
      Ok("It is working!")
    }
  }

Of course, the action generator method can contain parameters, and the Action closure can capture these parameters:

def hello(name: String) = Action {
  Ok("Hello " + name)
}

Redirects are simple results

Redirecting the browser to a new URL is another kind of simple result. However, these result types do not take a response body.

There are several helpers available to make redirect results:

def index = Action {
  Redirect("/user/home")
}

The default is to utilize a 303 SEE_OTHER response type, but you can also fix a more special status code if you need one:

def index = Action {
  Redirect("/user/home", MOVED_PERMANENTLY)
}

HTTP routing

The built-in HTTP router

The router is in charge of translating each incoming HTTP request into an Action.

An HTTP request is an event by the MVC framework. This event has 2 major pieces of information:

  • the request path (e.g. /clients/1542, /photos/list), along with the query string
  • the HTTP method (e.g. POST, GET…).

Routes are defined in the conf/routes file, which is compiled. This can mean that you will see route errors directly in your browser:

Compilation error scala

Image source

The HTTP method

The HTTP method can be any valid method supported by HTTP (GET, PATCH, POST, PUT, DELETE, HEAD).

Dependency Injection

The router class that is produced by Play's default routes generator has a function Object() { [native code] } that accepts controller instances and has the annotation @Inject. This indicates that the class is appropriate for dependency injection and that it can also be manually initialized using the function Object() { [native code] }.

Before version 2.7.0, Play permitted the definition of controllers as objects rather than classes through a static routes generator. Since Play no longer depends on a static state, that is no longer supported. You can still utilize your static state in a controller that is a class if you so choose.

Manipulating Results

Changing the default Content-Type

The result content type is automatically taken from the Scala value you specify as the response body.

For example:

val textResult = Ok("Hello World!")

Will automatically make/set the Content-Type header to text, while:

val xmlResult = Ok(<message>Hello World!</message>)

will make/set the Content-Type header to application/xml.

Manipulating HTTP headers

You can also add/update any HTTP header to the result:

val result = Ok("Hello World!").withHeaders(CACHE_CONTROL -> "max-age=870", ETAG -> "xx")

Note that setting an HTTP header will discard the previous value on its own if it was there in the original result.

Setting and discarding cookies

They (cookies) are just a special form of HTTP headers but we give a set of helpers to make it simpler.

You can add a Cookie to the HTTP response using:

val result = Ok("Hello world")
  .withCookies(Cookie("theme", "blue"))
  .bakeCookies()

Also, to discard a Cookie once stored on the Web browser:

val result2 = result.discardingCookies(DiscardingCookie("theme"))

We can also set and then remove cookies as part of the same response:

val result3 = result.withCookies(Cookie("theme", "blue")).discardingCookies(DiscardingCookie("skin"))

Session and Flash scopes

Working with Cookies

It's crucial to realize that Flash and session data are appended to each subsequent HTTP request using HTTP cookies rather than being saved on the server.

Some significant issues result from using cookies to implement Session and Flash.

  • The amount of data is relatively small (only up to 4 KB).
  • Despite being able to serialise JSON to the cookie, you can only store string data.
  • Cookies can disclose sensitive information because the browser can see the information inside.
  • Only subsequent requests can access cookie information, which is unchangeable for the initial request.

Confusion may occur about the final point. Play must analyze the response information again to see the changed value when you modify the cookie. If you want to ensure the session information is current, you should always pair modification of a session with a Redirect.

Storing data in the Session

As the Session is just a Cookie, it is also an HTTP header. You can control the session data the same way you manipulate other results properties:

Redirect("/home").withSession("connected" -> "user@gmail.com")

Note that this is used to replace the whole session. If you need to add an element to an existing Session, add an element to the incoming session, and define that as new session:

Redirect("/home").withSession(request.session + ("saidHello" -> "yes"))

You can clear any value from the incoming session the same way:

Redirect("/home").withSession(request.session - "theme")

Reading a Session value

You can get back the incoming Session from the HTTP request:

def index = Action { request =>
  request.session
    .get("connected")
    .map { user =>
      Ok("Hello " + user)
    }
    .getOrElse {
      Unauthorized("Oops, you are not connected")
    }
}

Discarding the whole session

There is special operation that discards the whole session:

Redirect("/home").withNewSession

Body parsers

An HTTP request contains a header and a content. The RequestHeader class in Play simulates the header since it is often tiny and can be securely buffered in memory. The body, on the other hand, has the potential to be quite long, thus it is treated as a stream rather than being buffered in memory. Play offers a BodyParser abstraction to map the body stream to an object in memory because many request body payloads are tiny and can be represented in memory.

The standard InputStream cannot be used to read the request body since it is blocking and must wait for data to be available. Play is an asynchronous framework, so this is not an option. 

Play instead makes use of the Akka Streams asynchronous streaming library. Although conventional InputStream-based technologies are not suitable for use with Play, Akka Streams and the entire ecosystem of asynchronous libraries around Reactive Streams will provide you with everything you need. Reactive Streams is an SPI that enables many asynchronous streaming APIs to seamlessly work together.

Action composition

This part introduces several ways of defining generic action functionality.

Custom action builders

The Action object we use to express our actions is simply an instance of the ActionBuilder trait, which defines all these methods for building actions. You can declare reusable action stacks that can be used to build actions by implementing your ActionBuilder.

Let's begin with a straightforward example of a logging decorator; we want to record each time this action is called.

The invokeBlock method, which the ActionBuilder uses to build each action, is the first place to implement this functionality.

import play.api.mvc._
class LoggingAction @Inject() (parser: BodyParsers.Default)(implicit ec: ExecutionContext)
    extends ActionBuilderImpl(parser)
    with Logging {
  override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    logger.info("Calling action")
    block(request)
  }
}

Now we can utilize Dependancy Injection in your controller to get an instance of the LoggingAction and use it the exact way we use Action:

class MyController @Inject() (loggingAction: LoggingAction, cc: ControllerComponents)
    extends AbstractController(cc) {
  def index = loggingAction {
    Ok("Hello World")
  }
}

Since ActionBuilder gives all the different methods of building actions, this also works with, for example, declaring a custom body parser:

def submit = loggingAction(parse.text) { request =>
  Ok("Got the body " + request.body.length + " bytes long")
}

Handling errors

Client errors and server errors are the two basic categories of errors that an HTTP application may return. Server errors show that the server is having a problem, while client errors show that the connecting client has made a mistake.

In many situations, Play will automatically identify client issues, including mistakes like incorrect header values, unsupported content types, and requests for resources that are not available. If your action code causes an exception, Play will catch it and create a server error page to transmit to the client. Play will also frequently automatically handle server failures.

The interface through which Play handles these errors is HttpErrorHandler. It defines two methods, onClientError, and onServerError.

Handling errors in a JSON API

By default, Play returns errors in an HTML format. For a JSON API, it’s more consistent to return errors in JSON. Play proposes an alternative HttpErrorHandler implementation, named JsonHttpErrorHandler, which will return errors formatted in JSON. To use that HttpErrorHandler implementation, you should configure the play.http.errorHandler configuration property in application.conf like this:

play.http.errorHandler = play.api.http.JsonHttpErrorHandler

Using both JSON and HTML and other content types

Suppose your application utilizes a mixture of JSON and HTML. In that case, as is prevalent in modern web apps, Play gives another error handler that delegates to either the JSON or HTML error handler based on the preferences given in the client’s Accept header. This can be shown with:

play.http.errorHandler = play.api.http.HtmlOrJsonHttpErrorHandler

This is a good default choice of error handler for many applications.

Frequently Asked Questions

Is Scala frontend or backend?

Scala. js is simply a frontend framework.

What is Scala and how it works?

Scala is an advanced version of Java that was made to eliminate unnecessary code. It supports multiple Libraries and APIs which will allow the programmer to achieve Less Down Time. 

What is HTTP?

The Hypertext Transfer Protocol is an application layer protocol in the Internet protocol suite model for distributed, collaborative, hypermedia information systems

Conclusion

In this article, we have discussed Scala developers- HTTP programming. We also looked at Actions, Controllers and Results, HTTP Routing, Manipulating HTTP results, Session and Flash scopes, Body parsers, Actions composition and Handling errors.

We hope this blog has helped you understand better. If you would like to learn more. Check out our articles on AWS, AWS Certification, and Cloud Computing. Practice makes a man perfect. To practice and improve yourself in the interview. You can check out Top 100 SQL problems, Interview experience, Coding interview questions, and the Ultimate guide path for interviews.

Thank You
Live masterclass