Play Framework 2.4 how to use return statement in Action controller

1.1k views Asked by At

is there a way to return a value inside Action controller.

I have a method in my User model which returns the number of friends of a given user.

def nrOfFriends(current_user: Long): Int = {
    DB.withConnection{ implicit connection =>
        var nr: Int = SQL("select count(*) from friend where user_id_1=" + current_user + " or user_id_2=" + current_user).as(scalar[Int].single)
        nr
    }
} 

In my controller, I just want to return the value from the model

def freunde() = IsAuthenticated { username => _ =>
    User.findByUsername(username).map { user =>
     var nr: Int = Friend.nrOfFriends(user.id.get)
        Ok(""+nr)

    }.getOrElse(Forbidden)
}

But in the way that is written, it will print "empty string " concatenated with the number

If I replace Ok(""+nr) with Ok(nr) I receive the following error:

"Cannot write an instance of Int to HTTP response. Try to define a Writeable[Int]"

I need for my action to return a value so that I can pass the value from the action to header.views.html inside the navbar something like that

   <a href="#">@Freund.freunde Friends</a>
2

There are 2 answers

1
gregghz On

if you want your response to just be the value of nr you can simply call nr.toString:

def freunde() = IsAuthenticated { username => _ =>
    User.findByUsername(username).map { user =>
     var nr: Int = Friend.nrOfFriends(user.id.get)
        Ok(nr.toString)

    }.getOrElse(Forbidden)
}

The error you're getting makes reference to the fact that Int doesn't have an implicit Writeable[Int] in scope. So play doesn't know how display an Int in an http response.

You can add make Int writeable by putting this in scope:

implicit val intWriteable = play.api.http.Writeable[Int](_.toString.getBytes, None)

Then you would be able to just say:

Ok(nr)

without error.

However, it sounds like you just want the result of nrOfFriends inside an unrelated template. If that's the case, you should be using an Action at all. Instead just call your model function inside the template where you need the data.

<a href="#">@User.nrOfFriends(user.id) Friends</a>

Of course you would need to pass in the user to the template as well.

You didn't post a full sample of all the code involved in what you are trying to accomplish so I think this is the best I can do for now. Perhaps try posting the base template that your <a> is in.

An important point is that Actions are for production an HTTP response, and not just plain data internally to the application.

0
Luong Ba Linh On

An action of a controller handles a request and generates a result to be sent to the client. In other words, an action returns a play.api.mvc.Result value, representing the HTTP response to send to the web client. In your example Ok constructs a 200 OK response. The body of the response must be one of the predefined types, including text/plain, json, and html. The number of a friends is an integer and is NOT an acceptable type of the body. Therefore, a simple way to address this problem is to convert it into a text/plain using .toString().

On the other hand, you can define a writer for Int that lets Play know how to convert an integer into a json format.

For more details, please take a look at this https://www.playframework.com/documentation/2.2.x/ScalaActions.