redirectToTrailingSlashIfMissing

Signature

def redirectToTrailingSlashIfMissing(redirectionType: StatusCodes.Redirection): Directive0

Description

If the requested path does not end with a trailing / character, redirects to the same path followed by such trailing slash.

Redirects the HTTP Client to the same resource yet followed by a trailing /, in case the request did not contain it. When redirecting an HttpResponse with the given redirect response code (i.e. MovedPermanently or TemporaryRedirect etc.) as well as a simple HTML page containing a “click me to follow redirect” link to be used in case the client can not, or refuses to for security reasons, automatically follow redirects.

Please note that the inner paths MUST end with an explicit trailing slash (e.g. "things"./) for the re-directed-to route to match.

See also redirectToNoTrailingSlashIfPresent which achieves the opposite - redirecting paths in case they do have a trailing slash.

Example

Scala
sourceimport akka.http.scaladsl.model.StatusCodes

val route =
  redirectToTrailingSlashIfMissing(StatusCodes.MovedPermanently) {
    concat(
      path("foo"./) {
        // We require the explicit trailing slash in the path
        complete("OK")
      },
      path("bad-1") {
        // MISTAKE!
        // Missing `/` in path, causes this path to never match,
        // because it is inside a `redirectToTrailingSlashIfMissing`
        ???
      },
      path("bad-2/") {
        // MISTAKE!
        // / should be explicit as path element separator and not *in* the path element
        // So it should be: "bad-2" /
        ???
      }
    )
  }

// tests:
// Redirected:
Get("/foo") ~> route ~> check {
  status shouldEqual StatusCodes.MovedPermanently

  // results in nice human readable message,
  // in case the redirect can't be followed automatically:
  responseAs[String] shouldEqual {
    "This and all future requests should be directed to " +
      "<a href=\"http://example.com/foo/\">this URI</a>."
  }
}

// Properly handled:
Get("/foo/") ~> route ~> check {
  status shouldEqual StatusCodes.OK
  responseAs[String] shouldEqual "OK"
}

// MISTAKE! will never match - reason explained in routes
Get("/bad-1/") ~> route ~> check {
  handled shouldEqual false
}

// MISTAKE! will never match - reason explained in routes
Get("/bad-2/") ~> route ~> check {
  handled shouldEqual false
}
Java
sourceimport static akka.http.javadsl.server.Directives.complete;
import static akka.http.javadsl.server.Directives.path;
import static akka.http.javadsl.server.Directives.redirectToTrailingSlashIfMissing;
import static akka.http.javadsl.server.Directives.route;

final Route route =
    redirectToTrailingSlashIfMissing(
        StatusCodes.MOVED_PERMANENTLY, () ->
        concat(
            path(segment("foo").slash(), () -> complete("OK")),
            path(segment("bad-1"), () ->
                // MISTAKE!
                // Missing .slash() in path, causes this path to never match,
                // because it is inside a `redirectToTrailingSlashIfMissing`
                complete(StatusCodes.NOT_IMPLEMENTED)
            ),
            path(segment("bad-2/"), () ->
                // MISTAKE!
                // / should be explicit with `.slash()` and not *in* the path element
                // So it should be: segment("bad-2").slash()
                complete(StatusCodes.NOT_IMPLEMENTED)
            )
        )
    );
// tests:
testRoute(route).run(HttpRequest.GET("/foo"))
    .assertStatusCode(StatusCodes.MOVED_PERMANENTLY)
    .assertEntity("This and all future requests should be directed to " +
      "<a href=\"http://example.com/foo/\">this URI</a>.");

testRoute(route).run(HttpRequest.GET("/foo/"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("OK");

testRoute(route).run(HttpRequest.GET("/bad-1/"))
    .assertStatusCode(StatusCodes.NOT_FOUND);
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.