From 502b1027b69dc071bfc141eb47c1e8d6b707c74d Mon Sep 17 00:00:00 2001 From: Gmugra Date: Mon, 28 Dec 2020 19:15:49 +0100 Subject: [PATCH] added redirect methods in the Response class --- .../net/cactusthorn/routing/Response.java | 53 ++++++++++++++++++- .../cactusthorn/routing/RoutingServlet.java | 16 ++++-- .../net/cactusthorn/routing/ResponseTest.java | 42 +++++++++++++++ .../routing/RoutingServletTest.java | 23 ++++++++ .../jetty/entrypoint/SimpleEntryPoint.java | 9 ++++ 5 files changed, 137 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/net/cactusthorn/routing/Response.java b/core/src/main/java/net/cactusthorn/routing/Response.java index ee17538..2e926e2 100644 --- a/core/src/main/java/net/cactusthorn/routing/Response.java +++ b/core/src/main/java/net/cactusthorn/routing/Response.java @@ -1,5 +1,6 @@ package net.cactusthorn.routing; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -8,9 +9,28 @@ import java.util.Optional; import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; public final class Response { + static class Redirect { + private int code; + private URI uri; + + Redirect(int code, URI uri) { + this.code = code; + this.uri = uri; + } + + int code() { + return code; + } + + URI uri() { + return uri; + } + } + private int statusCode; private Object body; private String contentType; @@ -22,6 +42,8 @@ public final class Response { private Map> intHeaders = new HashMap<>(); private Map> dateHeaders = new HashMap<>(); + private Redirect redirect; + // @formatter:off private Response( int statusCode, @@ -33,7 +55,8 @@ private Response( List cookies, Map> headers, Map> intHeaders, - Map> dateHeaders) { + Map> dateHeaders, + Redirect redirect) { this.statusCode = statusCode; this.body = body; this.contentType = contentType; @@ -44,6 +67,7 @@ private Response( this.headers = headers; this.intHeaders = intHeaders; this.dateHeaders = dateHeaders; + this.redirect = redirect; } // @formatter:on @@ -91,6 +115,10 @@ public Map> dateHeaders() { return dateHeaders; } + public Optional redirect() { + return Optional.ofNullable(redirect); + } + public static final class Builder { private static final int DEFAULT_STATUS_CODE = 200; @@ -105,6 +133,7 @@ public static final class Builder { private final Map> intHeaders = new HashMap<>(); private final Map> dateHeaders = new HashMap<>(); private boolean skipProducer; + private Redirect redirect; private Builder() { } @@ -282,13 +311,33 @@ public Builder addDateHeader(String name, long date) { return this; } + public Builder movedTemporarily(URI uri) { + redirect = new Redirect(HttpServletResponse.SC_MOVED_TEMPORARILY, uri); + return this; + } + + public Builder movedPermanently(URI uri) { + redirect = new Redirect(HttpServletResponse.SC_MOVED_PERMANENTLY, uri); + return this; + } + + public Builder seeOther(URI uri) { + redirect = new Redirect(HttpServletResponse.SC_SEE_OTHER, uri); + return this; + } + + public Builder redirect(int statusCode, URI uri) { + redirect = new Redirect(statusCode, uri); + return this; + } + public Response build() { List unmodifiableCookies = Collections.unmodifiableList(cookies); Map> unmodifiableHeaders = Collections.unmodifiableMap(headers); Map> unmodifiableIntHeaders = Collections.unmodifiableMap(intHeaders); Map> unmodifiableDateHeaders = Collections.unmodifiableMap(dateHeaders); return new Response(statuscode, bbody, contenttype, ttemplate, characterencoding, skipProducer, unmodifiableCookies, - unmodifiableHeaders, unmodifiableIntHeaders, unmodifiableDateHeaders); + unmodifiableHeaders, unmodifiableIntHeaders, unmodifiableDateHeaders, redirect); } } diff --git a/core/src/main/java/net/cactusthorn/routing/RoutingServlet.java b/core/src/main/java/net/cactusthorn/routing/RoutingServlet.java index 6ea8fde..e43182a 100644 --- a/core/src/main/java/net/cactusthorn/routing/RoutingServlet.java +++ b/core/src/main/java/net/cactusthorn/routing/RoutingServlet.java @@ -14,6 +14,7 @@ import net.cactusthorn.routing.EntryPointScanner.EntryPoint; import net.cactusthorn.routing.RoutingConfig.ConfigProperty; import net.cactusthorn.routing.PathTemplate.PathValues; +import net.cactusthorn.routing.Response.Redirect; import net.cactusthorn.routing.annotation.*; import net.cactusthorn.routing.convert.ConverterException; import net.cactusthorn.routing.producer.Producer; @@ -155,21 +156,28 @@ private void produce(HttpServletRequest req, HttpServletResponse resp, EntryPoin private void produce(HttpServletRequest req, HttpServletResponse resp, EntryPoint entryPoint, Response response) throws IOException { - if (response.statusCode() != HttpServletResponse.SC_OK) { - resp.setStatus(response.statusCode()); - } - response.cookies().forEach(c -> resp.addCookie(c)); response.headers().entrySet().forEach(e -> e.getValue().forEach(v -> resp.addHeader(e.getKey(), v))); response.intHeaders().entrySet().forEach(e -> e.getValue().forEach(v -> resp.addIntHeader(e.getKey(), v))); response.dateHeaders().entrySet().forEach(e -> e.getValue().forEach(v -> resp.addDateHeader(e.getKey(), v))); + if (response.redirect().isPresent()) { + Redirect redirect = response.redirect().get(); + resp.setHeader("Location", redirect.uri().toString()); + resp.setStatus(redirect.code()); + return; + } + resp.setCharacterEncoding(response.characterEncoding().orElse(responseCharacterEncoding)); String contentType = response.contentType().orElse(entryPoint.produces()); resp.setContentType(contentType); + if (response.statusCode() != HttpServletResponse.SC_OK) { + resp.setStatus(response.statusCode()); + } + String template = response.template().orElse(entryPoint.template()); if (response.skipProducer()) { diff --git a/core/src/test/java/net/cactusthorn/routing/ResponseTest.java b/core/src/test/java/net/cactusthorn/routing/ResponseTest.java index ef214ce..c3c121e 100644 --- a/core/src/test/java/net/cactusthorn/routing/ResponseTest.java +++ b/core/src/test/java/net/cactusthorn/routing/ResponseTest.java @@ -2,10 +2,16 @@ import static org.junit.jupiter.api.Assertions.*; +import java.net.URI; +import java.net.URISyntaxException; + import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; import org.junit.jupiter.api.Test; +import net.cactusthorn.routing.Response.Redirect; + public class ResponseTest { @Test // @@ -88,4 +94,40 @@ public void skipProducer() { response = Response.builder().skipProducer().build(); assertTrue(response.skipProducer()); } + + @Test // + public void seeOther() throws URISyntaxException { + URI uri = new URI("/xxx"); + Response response = Response.builder().seeOther(uri).build(); + Redirect redirect = response.redirect().get(); + assertEquals(HttpServletResponse.SC_SEE_OTHER, redirect.code()); + assertEquals(uri, redirect.uri()); + } + + @Test // + public void movedTemporarily() throws URISyntaxException { + URI uri = new URI("/yyy"); + Response response = Response.builder().movedTemporarily(uri).build(); + Redirect redirect = response.redirect().get(); + assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, redirect.code()); + assertEquals(uri, redirect.uri()); + } + + @Test // + public void movedPermanently() throws URISyntaxException { + URI uri = new URI("/zzz"); + Response response = Response.builder().movedPermanently(uri).build(); + Redirect redirect = response.redirect().get(); + assertEquals(HttpServletResponse.SC_MOVED_PERMANENTLY, redirect.code()); + assertEquals(uri, redirect.uri()); + } + + @Test // + public void redirect() throws URISyntaxException { + URI uri = new URI("/rrr"); + Response response = Response.builder().redirect(300, uri).build(); + Redirect redirect = response.redirect().get(); + assertEquals(300, redirect.code()); + assertEquals(uri, redirect.uri()); + } } diff --git a/core/src/test/java/net/cactusthorn/routing/RoutingServletTest.java b/core/src/test/java/net/cactusthorn/routing/RoutingServletTest.java index 0f8b43a..da5a5e0 100644 --- a/core/src/test/java/net/cactusthorn/routing/RoutingServletTest.java +++ b/core/src/test/java/net/cactusthorn/routing/RoutingServletTest.java @@ -5,6 +5,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.net.URI; +import java.net.URISyntaxException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -107,6 +109,11 @@ public Response responseAll() { return Response.builder().skipProducer().setBody("FROM RESPONSE").setCharacterEncoding("KOI8-R").setTemplate("TTT") .setStatus(201).addCookie(cookie).addHeader("h", "v").addIntHeader("hi", 10).addDateHeader("hd", 20L).build(); } + + @GET @Path("api/redirect") // + public Response redirect() throws URISyntaxException { + return Response.builder().seeOther(new URI("/xyz")).build(); + } } public static class EntryPoint1Provider implements ComponentProvider { @@ -357,4 +364,20 @@ public void responseAll() throws ServletException, IOException { assertEquals("hd", dateHeaderName.getValue()); assertEquals(20L, dateHeaderValue.getValue()); } + + @Test // + public void redirect() throws ServletException, IOException { + Mockito.when(req.getPathInfo()).thenReturn("/api/redirect"); + + servlet.doGet(req, resp); + + ArgumentCaptor header = ArgumentCaptor.forClass(String.class); + ArgumentCaptor code = ArgumentCaptor.forClass(Integer.class); + + Mockito.verify(resp).setStatus(code.capture()); + Mockito.verify(resp).setHeader(Mockito.eq("Location"), header.capture()); + + assertEquals("/xyz", header.getValue()); + assertEquals(303, code.getValue()); + } } diff --git a/demo-jetty/src/main/java/net/cactusthorn/routing/demo/jetty/entrypoint/SimpleEntryPoint.java b/demo-jetty/src/main/java/net/cactusthorn/routing/demo/jetty/entrypoint/SimpleEntryPoint.java index 55856ec..9acebdf 100644 --- a/demo-jetty/src/main/java/net/cactusthorn/routing/demo/jetty/entrypoint/SimpleEntryPoint.java +++ b/demo-jetty/src/main/java/net/cactusthorn/routing/demo/jetty/entrypoint/SimpleEntryPoint.java @@ -1,7 +1,11 @@ package net.cactusthorn.routing.demo.jetty.entrypoint; +import java.net.URI; +import java.net.URISyntaxException; + import javax.inject.Inject; +import net.cactusthorn.routing.Response; import net.cactusthorn.routing.annotation.DefaultValue; import net.cactusthorn.routing.annotation.GET; import net.cactusthorn.routing.annotation.Path; @@ -33,4 +37,9 @@ public String doit(@PathParam("var") int in, @DefaultValue("10.5") @QueryParam(" public String empty(@PathParam("var") @DefaultValue("DEFAULT") String sss) { return "|" + sss + "| :: " + this.getClass().getSimpleName() + "@" + this.hashCode(); } + + @GET @Path("/seeother") // + public Response seeother() throws URISyntaxException { + return Response.builder().seeOther(new URI("/rest/api/test30?test=33.45")).build(); + } }