All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mangoo.routing.Response Maven / Gradle / Ivy

The newest version!
package io.mangoo.routing;

import com.google.common.net.MediaType;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.mangoo.constants.Header;
import io.mangoo.constants.NotNull;
import io.mangoo.constants.Template;
import io.mangoo.models.Error;
import io.mangoo.utils.JsonUtils;
import io.undertow.server.handlers.Cookie;
import io.undertow.server.handlers.CookieImpl;
import io.undertow.util.HttpString;
import io.undertow.util.StatusCodes;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class Response {
    private static final Logger LOG = LogManager.getLogger(Response.class);
    private final Map headers = new HashMap<>();
    private final Map content = new HashMap<>();
    private final List cookies = new ArrayList<>();
    private String redirectTo;
    private String contentType = MediaType.HTML_UTF_8.withoutParameters().toString();
    private String body = Strings.EMPTY;
    private String template;
    private String binaryFileName;
    private byte[] binaryContent;
    private boolean endResponse;
    private boolean binary;
    private boolean rendered;
    private boolean redirect;
    private int statusCode = StatusCodes.OK;

    public Response() {
        //Empty constructor for Google Guice
    }

    private Response(int statusCode, boolean rendered) {
        this.statusCode = statusCode;
        this.rendered = rendered;
    }

    private Response(String redirectTo) {
        Objects.requireNonNull(redirectTo, NotNull.REDIRECT_TO);
        
        this.redirect = true;
        this.rendered = false;
        this.redirectTo = redirectTo;
    }

    /**
     * Creates a response object with HTTP status code 200
     * @deprecated As of release 8.6.0, replaced by {@link #ok()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withOk() {
        return new Response(StatusCodes.OK, true);
    }

    /**
     * Creates a response object with HTTP status code 200
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response ok() {
        return new Response(StatusCodes.OK, true);
    }

    /**
     * Creates a response object with HTTP status code 201
     * @deprecated As of release 8.6.0, replaced by {@link #created()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withCreated() {
        return new Response(StatusCodes.CREATED, true);
    }

    /**
     * Creates a response object with HTTP status code 201
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response created() {
        return new Response(StatusCodes.CREATED, true);
    }

    /**
     * Creates a response object with HTTP status code 404
     * @deprecated As of release 8.6.0, replaced by {@link #notFound()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withNotFound() {
        return new Response(StatusCodes.NOT_FOUND, true);
    }

    /**
     * Creates a response object with HTTP status code 404
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response notFound() {
        return new Response(StatusCodes.NOT_FOUND, true);
    }

    /**
     * Creates a response object with HTTP status code 401
     * @deprecated As of release 8.6.0, replaced by {@link #forbidden()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withForbidden() {
        return new Response(StatusCodes.FORBIDDEN, true);
    }

    /**
     * Creates a response object with HTTP status code 401
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response forbidden() {
        return new Response(StatusCodes.FORBIDDEN, true);
    }

    /**
     * Creates a response object with HTTP status code 403
     * @deprecated As of release 8.6.0, replaced by {@link #unauthorized()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withUnauthorized() {
        return new Response(StatusCodes.UNAUTHORIZED, true);
    }

    /**
     * Creates a response object with HTTP status code 403
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response unauthorized() {
        return new Response(StatusCodes.UNAUTHORIZED, true);
    }

    /**
     * Creates a response object with HTTP status code 400
     * @deprecated As of release 8.6.0, replaced by {@link #badRequest()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withBadRequest() {
        return new Response(StatusCodes.BAD_REQUEST, true);
    }

    /**
     * Creates a response object with HTTP status code 400
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response badRequest() {
        return new Response(StatusCodes.BAD_REQUEST, true);
    }
    
    /**
     * Creates a response object with HTTP status code 500
     * @deprecated As of release 8.6.0, replaced by {@link #internalServerError()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withInternalServerError() {
        return new Response(StatusCodes.INTERNAL_SERVER_ERROR, true);
    }

    /**
     * Creates a response object with HTTP status code 500
     * and rendering a response body from a template
     *
     * @return The response object
     */
    public static Response internalServerError() {
        return new Response(StatusCodes.INTERNAL_SERVER_ERROR, true);
    }
    
    /**
     * Creates a response object with a given HTTP status code
     * @deprecated As of release 8.6.0, replaced by {@link #status(int)}
     *
     * @param statusCode The status code to set
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withStatusCode(int statusCode) {
        return new Response(statusCode, true);
    }

    /**
     * Creates a response object with a given HTTP status code
     * and rendering a response body from a template
     *
     * @param statusCode The status code to set
     * @return The response object
     */
    public static Response status(int statusCode) {
        return new Response(statusCode, true);
    }

    /**
     * Creates a response object with a given url to redirect to
     * @deprecated As of release 8.6.0, replaced by {@link #redirect(String)}
     *
     * @param redirectTo The URL to redirect to
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public static Response withRedirect(String redirectTo) {
        return redirect(redirectTo);
    }

    /**
     * Creates a response object with a given url to redirect to
     *
     * @param redirectTo The URL to redirect to
     * @return The response object
     */
    public static Response redirect(String redirectTo) {
        Objects.requireNonNull(redirectTo, NotNull.REDIRECT_TO);

        return new Response(redirectTo);
    }

    public int getStatusCode() {
        return statusCode;
    }

    public String getContentType() {
        return contentType;
    }

    public String getBody() {
        return body;
    }

    public List getCookies() {
        return new ArrayList<>(cookies);
    }

    public byte[] getBinaryContent() {
        return binaryContent.clone();
    }

    public String getTemplate() {
        return template;
    }

    public String getBinaryFileName() {
        return binaryFileName;
    }

    public Map getContent() {
        return content;
    }

    public boolean isRedirect() {
        return redirect;
    }

    public boolean isBinary() {
        return binary;
    }

    public boolean isRendered() {
        return rendered;
    }

    public boolean isEndResponse() {
        return endResponse;
    }

    public String getRedirectTo() {
        return redirectTo;
    }

    public Map getHeaders() {
        return headers;
    }

    public String getHeader(HttpString header) {
        Objects.requireNonNull(header, NotNull.HEADER);
        return headers.get(header);
    }

    /**
     * Sets a specific template to use for the response
     * @deprecated As of release 8.6.0, replaced by {@link #template(String)}
     *
     * @param template The path to the template (e.g. /mytemplate/template.ftl)
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andTemplate(String template) {
        Objects.requireNonNull(template, NotNull.TEMPLATE);
        this.template = template;

        return this;
    }

    /**
     * Sets a specific template to use for the response
     *
     * @param template The path to the template (e.g. /path-to-template/template.ftl)
     * @return The response object
     */
    public Response template(String template) {
        Objects.requireNonNull(template, NotNull.TEMPLATE);
        this.template = template;

        return this;
    }

    /**
     * Sets a specific content type to use for the response. Default is "text/html"
     * @deprecated As of release 8.6.0, replaced by {@link #contentType(String)}
     *
     * @param contentType The content type to use
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andContentType(String contentType) {
        Objects.requireNonNull(contentType, NotNull.CONTENT_TYPE);
        
        headers.put(Header.CONTENT_TYPE, contentType);
        this.contentType = contentType;

        return this;
    }

    /**
     * Sets a specific content type to use for the response. Default is "text/html"
     *
     * @param contentType The content type to use
     * @return The response object
     */
    public Response contentType(String contentType) {
        Objects.requireNonNull(contentType, NotNull.CONTENT_TYPE);

        headers.put(Header.CONTENT_TYPE, contentType);
        this.contentType = contentType;

        return this;
    }

    /**
     * Adds a value to the template that can be accessed using ${name} in the template
     * @deprecated As of release 8.6.0, replaced by {@link #render(String, Object)}
     *
     * @param name The name of the value
     * @param object The actual value
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andContent(String name, Object object) {
        Objects.requireNonNull(name, NotNull.NAME);
        content.put(name, object);

        return this;
    }

    /**
     * Adds a value to the template that can be accessed using ${name} in the template
     *
     * @param name The name of the value
     * @param object The actual value
     * @return The response object
     */
    public Response render(String name, Object object) {
        Objects.requireNonNull(name, NotNull.NAME);
        content.put(name, object);

        return this;
    }

    /**
     * Sets the body of the response. If a body is added, no template rendering will be
     * performed. The default content type "text/html" will be used.
     * @deprecated As of release 8.6.0, replaced by {@link #bodyHtml(String)}
     *
     * @param html The html for the body
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andHtmlBody(String html) {
        this.body = html;
        rendered = false;

        return this;
    }

    /**
     * Sets the body of the response. If a body is added, no template rendering will be
     * performed. The default content type "text/html" will be used.
     *
     * @param html The html for the body
     * @return The response object
     */
    public Response bodyHtml(String html) {
        this.body = html;
        rendered = false;

        ok();
        unauthorized();
        forbidden();
        internalServerError();
        badRequest();

        return this;
    }

    public Response bodyDefault() {
        switch (statusCode) {
            case StatusCodes.OK:
                this.body = Template.ok();
                break;
            case StatusCodes.UNAUTHORIZED:
                this.body = Template.unauthorized();
                break;
            case StatusCodes.NOT_FOUND:
                this.body = Template.notFound();
                break;
            case StatusCodes.FORBIDDEN:
                this.body = Template.forbidden();
                break;
            case StatusCodes.INTERNAL_SERVER_ERROR:
                this.body = Template.internalServerError();
                break;
            case StatusCodes.BAD_REQUEST:
                this.body = Template.badRequest();
                break;
            default:
                this.body = Template.xxx().replace("###xxx###", String.valueOf(statusCode));
        }
        rendered = false;

        return this;
    }

    /**
     * Adds a Cookie to the response which is passed to the client
     * @deprecated As of release 8.6.0, replaced by {@link #cookie(Cookie)}
     *
     * @param cookie The cookie to add
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andCookie(Cookie cookie) {
        Objects.requireNonNull(cookie, NotNull.COOKIE);
        cookies.add(cookie);

        return this;
    }

    /**
     * Adds a Cookie to the response which is passed to the client
     *
     * @param cookie The cookie to add
     * @return The response object
     */
    public Response cookie(Cookie cookie) {
        Objects.requireNonNull(cookie, NotNull.COOKIE);
        cookies.add(cookie);

        return this;
    }

    /**
     * Converts a given Object to JSON and passing it to the response. If an object is given, no
     * template rendering will be performed and the content type for the response will be set to
     * "application/json"
     * @deprecated As of release 8.6.0, replaced by {@link #bodyJson(Object)}
     *
     * @param jsonObject The object to convert to JSON
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andJsonBody(Object jsonObject) {
        Objects.requireNonNull(jsonObject, NotNull.JSON_OBJECT);

        this.body = JsonUtils.toJson(jsonObject);
        contentType = MediaType.JSON_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Converts a given Object to JSON and passing it to the response. If an object is given, no
     * template rendering will be performed and the content type for the response will be set to
     * "application/json"
     *
     * @param object The object to convert to JSON
     * @return The response object
     */
    public Response bodyJson(Object object) {
        Objects.requireNonNull(object, NotNull.OBJECT);

        this.body = JsonUtils.toJson(object);
        contentType = MediaType.JSON_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Sets a JSON error string as body
     * @param message The error message to return
     * @return The response object
     */
    public Response bodyJsonError(String message) {
        Objects.requireNonNull(message, NotNull.MESSAGE);

        this.body = JsonUtils.toJson(Error.of(message, statusCode));
        contentType = MediaType.JSON_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }
    
    /**
     * Sets a given JSON string as body. If a String is given, no template rendering will be
     * performed and the content type for the response will be set to "application/json"
     * @deprecated As of release 8.6.0, replaced by {@link #bodyJson(String)}
     *
     * @param json The String to set as JSON
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andJsonBody(String json) {
        Objects.requireNonNull(json, NotNull.JSON);

        this.body = json;
        contentType = MediaType.JSON_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Sets a given JSON string as body. If a String is given, no template rendering will be
     * performed and the content type for the response will be set to "application/json"
     *
     * @param json The String to set as JSON
     * @return The response object
     */
    public Response bodyJson(String json) {
        Objects.requireNonNull(json, NotNull.JSON);

        this.body = json;
        contentType = MediaType.JSON_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Sends a binary file to the client skipping rendering
     * @deprecated As of release 8.6.0 with not replacement
     *
     * @param file The file to send
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    @SuppressFBWarnings(justification = "null check of file on entry point of method", value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
    public Response andBinaryFile(Path file) {
        Objects.requireNonNull(file, NotNull.FILE);

        try (var inputStream = Files.newInputStream(file)) {
            binaryFileName = file.getFileName().toString();
            binaryContent = IOUtils.toByteArray(inputStream);
            binary = true;
            rendered = false;
        } catch (final IOException e) {
            LOG.error("Failed to handle binary file", e);
        }

        return this;
    }

    /**
     * Sends binary content to the client skipping rendering
     * @deprecated As of release 8.6.0 without replacement
     *
     * @param content The content to send
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andBinaryContent(byte [] content) {
        Objects.requireNonNull(content, NotNull.CONTENT);

        binaryContent = content.clone();
        binary = true;
        rendered = false;

        return this;
    }

    /**
     * Sets the body of the response. If a body is added, no template rendering will be
     * performed. The content type "text/plain" will be used.
     * @deprecated As of release 8.6.0, replaced by {@link #bodyText(String)}
     *
     * @param body The text for the response
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andTextBody(String body) {
        this.body = body;
        contentType = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Sets the body of the response. If a body is added, no template rendering will be
     * performed. The content type "text/plain" will be used.
     *
     * @param text The text for the body
     *
     * @return The response object
     */
    public Response bodyText(String text) {
        this.body = text;
        contentType = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Disables template rendering, sending an empty body with content-type
     * text/plain in the response
     * @deprecated As of release 8.6.0, replaced by {@link #bodyEmpty()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andEmptyBody() {
        contentType = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Disables template rendering, sending an empty body with content-type
     * text/plain in the response
     *
     * @return The response object
     */
    public Response bodyEmpty() {
        contentType = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
        rendered = false;

        return this;
    }

    /**
     * Adds a header to the request response. If a header
     * key already exists, it will be overwritten with the latest value.
     * @deprecated As of release 8.6.0, replaced by {@link #header(String, String)}
     *
     * @param key The header key
     * @param value The header value
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andHeader(String key, String value) {
        Objects.requireNonNull(key, NotNull.KEY);
        headers.put(new HttpString(key), value);

        return this;
    }

    /**
     * Adds a header to the request response. If a header
     * key already exists, it will be overwritten with the latest value.
     *
     * @param key The header key
     * @param value The header value
     *
     * @return The response object
     */
    public Response header(String key, String value) {
        Objects.requireNonNull(key, NotNull.KEY);
        headers.put(new HttpString(key), value);

        return this;
    }

    /**
     * Adds a content map to the content rendered in the template.
     * Already existing values with the same key are overwritten.
     * @deprecated As of release 8.6.0, replaced by {@link #render(Map)}
     *
     * @param content The content map to add
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andContent(Map content) {
        Objects.requireNonNull(content, NotNull.CONTENT);
        this.content.putAll(content);

        return this;
    }

    /**
     * Adds a content map to the content rendered in the template.
     * Already existing values with the same key are overwritten.
     *
     * @param content The content map to add
     * @return The response object
     */
    public Response render(Map content) {
        Objects.requireNonNull(content, NotNull.CONTENT);
        this.content.putAll(content);

        return this;
    }

    /**
     * Adds a header map to the response.
     * Already existing values with the same key are overwritten.
     * @deprecated As of release 8.6.0, replaced by {@link #headers(Map)}
     *
     * @param headers The headers map to add
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andHeaders(Map headers) {
        Objects.requireNonNull(headers, NotNull.HEADERS);
        this.headers.putAll(headers);

        return this;
    }

    /**
     * Adds a header map to the response.
     * Already existing values with the same key are overwritten.
     *
     * @param headers The headers map to add
     * @return The response object
     */
    public Response headers(Map headers) {
        Objects.requireNonNull(headers, NotNull.HEADERS);
        this.headers.putAll(headers);

        return this;
    }
    
    /**
     * Disposes a cookie by setting the expired date of the give cookie name
     * to a date in the past, max age to -1 and an empty value
     * @deprecated As of release 8.6.0, replaced by {@link #disposeCookie(String)}
     * 
     * @param cookieName The name of the cookie to dispose
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andDisposeCookie(String cookieName) {
        Objects.requireNonNull(cookieName, NotNull.COOKIE);
        
        cookies.add(new CookieImpl(cookieName)
                .setPath("/")
                .setValue("")
                .setMaxAge(-1)
                .setDiscard(true)
                .setExpires(new Date(1)));
        
        return this;
    }

    /**
     * Disposes a cookie by setting the expired date of the give cookie name
     * to a date in the past, max age to -1 and an empty value
     *
     * @param cookieName The name of the cookie to dispose
     * @return The response object
     */
    public Response disposeCookie(String cookieName) {
        Objects.requireNonNull(cookieName, NotNull.COOKIE);

        cookies.add(new CookieImpl(cookieName)
                .setPath("/")
                .setValue("")
                .setMaxAge(-1)
                .setDiscard(true)
                .setExpires(new Date(1)));

        return this;
    }

    /**
     * Tells a filter that the response ends and that the request handler
     * should not execute further filters by sending the current response
     * to the client. This is only used within a filter.
     * @deprecated As of release 8.6.0, replaced by {@link #end()}
     *
     * @return The response object
     */
    @Deprecated(since = "8.6.0", forRemoval = true)
    public Response andEndResponse() {
        endResponse = true;

        return this;
    }

    /**
     * Tells a filter that the response ends and that the request handler
     * should not execute further filters by sending the current response
     * to the client. This is only used within a filter.
     *
     * @return The response object
     */
    public Response end() {
        endResponse = true;

        return this;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy