How to create a custom 404 page handler with Play 2.0?
Asked Answered
R

7

27

What’s the preferred way to handle 404 errors with Play 2.0 and show a nice templated view?

Riding answered 3/4, 2012 at 8:10 Comment(0)
R
24

You can override the onHandlerNotFound method on your Global object, e.g.:

object Global extends GlobalSettings {
  override def onHandlerNotFound(request: RequestHeader): Result = {
    NotFound(views.html.notFound(request))
  }
}
Riding answered 3/4, 2012 at 8:18 Comment(4)
did you create a page yourself? When I tried that code it's saying it doesn't work?Aplomb
Yes, this code assumes there is a notFound.scala.html template in your views/ directory.Riding
@JulienRichard-Foy: Please be aware that your solution works on 2.0.4 only. In 2.0 it's onActionNotFound, see the differences: playframework.org/documentation/2.0/JavaGlobal playframework.org/documentation/2.0.4/JavaGlobalBranham
Note that in Play 2.2.x the return type has changed so it should be wrapped in a Future. playframework.com/documentation/2.2.x/ScalaGlobalSpann
D
20

Please note that there are really two different problems to solve:

  1. Showing a custom 404 page when there is "no handler found", e.g. when the user goes to an invalid URL, and

  2. Showing a custom 404 (NotFound) page as a valid outcome of an existing handler.

I think the OP was referring to #2 but answers referred to #1.

"No Handler Found" Scenario

In the first scenario, for "no handler found" (i.e. invalid URL), the other answers have it right but to be more detailed, per the Play 2.1 documentation as:

Step 1: add a custom Global object:

import play.api._
import play.api.mvc._
import play.api.mvc.Results._

object Global extends GlobalSettings {

  override def onHandlerNotFound(request: RequestHeader): Result = {
    NotFound(
      views.html.notFoundPage(request.path)
    )
  }   
}

Step 2: add the template. Here's mine:

@(path: String)

<html>
<body>
<h1>Uh-oh. That wasn't found.</h1>
<p>@path</p>
</body>
</html>

Step 3: tweak your conf/application.conf to refer to your new "Global". I put it in the controllers package but it doesn't have to be:

...
application.global=controllers.Global

Step 4: restart and go to an invalid URL.

"Real Handler can't find object" Scenario

In the second scenario an existing handler wants to show a custom 404. For example, the user asked for object "1234" but no such object exists. The good news is that doing this is deceptively easy:

Instead of Ok(), surround your response with NotFound()

For example:

object FruitController extends Controller {

  def showFruit(uuidString: String) = Action {
    Fruits.find(uuidString) match {
      case Some(fruit) => Ok(views.html.showFruit(fruit))

      // NOTE THE USE OF "NotFound" BELOW!
      case None => NotFound(views.html.noSuchFruit(s"No such fruit: $uuidString"))
    }
  }
}

What I like about this is the clean separation of the status code (200 vs 404) from the HTML returned (showFruit vs noSuchFruit).

HTH Andrew

Definitive answered 21/8, 2013 at 2:53 Comment(2)
You can skip your step 3 if you put your Global object in the root package, i.e. app/views/Global.scala instead of app/views/controllers/Global.scala like in your example. The default application.conf points there: # application.global=GlobalRadnorshire
In Play 2.2, the response must be wrapped in a Future: playframework.com/documentation/2.2.x/ScalaGlobalRadnorshire
B
6

If you want to do the same using Java instead of Scala you can do it in this way (this works for play framework 2.0.3):

Global.java:

import play.GlobalSettings;
import play.mvc.Result;
import play.mvc.Results;
import play.mvc.Http.RequestHeader;


public class Global extends GlobalSettings {

    @Override
    public Result onHandlerNotFound(RequestHeader request) {
        return Results.notFound(views.html.error404.render());
    }
}

Asumming that your 404 error template is views.html.error404 (i.e. views/error404.scala.html).

Bloomer answered 28/2, 2013 at 23:7 Comment(0)
F
3

Please note that Play development team are making lots of efforts to move away from global state in Play, and hence GlobalSettings and the application Global object have been deprecated since version 2.4.

HttpErrorHandler.onClientError should be used instead of GlobalSettings.onHandlerNotFound. Basically create a class that inherits from HttpErrorHandler, and provide an implementation for onClientError method.

In order to find out type of error (404 in your case) you need to read status code, which is passed as a one of the method arguments e.g.

if(statusCode == play.mvc.Http.Status.NOT_FOUND) {
    // your code to handle 'page not found' situation 
    // e.g. return custom implementation of 404 page
}

In order to let Play know what handler to use, you can place your error handler in the root package or configure it in application.conf using play.http.errorHandler configuration key e.g.

play.http.errorHandler = "my.library.MyErrorHandler"

You can find more details on handling errors here: for Scala or Java.

Footman answered 14/9, 2016 at 10:48 Comment(1)
R
2

This works in 2.2.1. In Global.java:

public Promise<SimpleResult> onHandlerNotFound(RequestHeader request) {
    return Promise.<SimpleResult>pure(notFound(
        views.html.throw404.render()
    ));
}

Ensure that you have a view called /views/throw404.scala.html

Rosenda answered 6/2, 2014 at 3:33 Comment(0)
T
0

This works in 2.2.3 Play - Java

public Promise<SimpleResult> onHandlerNotFound(RequestHeader request) {
    return Promise<SimpleResult>pure(Results.notFound(views.html.notFound404.render()));
}

html should be within /views/notFound404.scala.html Dont forget to add Results.notFounf() and import play.mvc.Results;

Teaspoon answered 12/5, 2014 at 8:39 Comment(1)
Your question does not contain any information that is not already provided by at least one of the other answers to this question.Maragretmarala
C
0

For Java, if you want to just redirect to main page, I solved it by this.

@Override
public Promise<Result> onHandlerNotFound(RequestHeader request) {
    return Promise.pure(redirect("/"));
}
Catabolite answered 18/5, 2017 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.