Repository: tipsy/javalin
Pull request #1351 (2021-08-17 23:46:24)

GH-1342 Use ContentType rather than raw strings if possible


Patch:
diff --git a/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLPlugin.kt b/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLPlugin.kt
index 58bda750..59cfba06 100644
--- a/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLPlugin.kt
+++ b/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLPlugin.kt
@@ -18,6 +18,7 @@ class GraphQLPlugin(private val builder: GraphQLPluginBuilder<*>) : Plugin, Plug
 
     override fun apply(app: Javalin) {
         val server = JavalinGraphQLServer.create(builder)
+
         app.get(builder.path) {
             it.contentType("text/html; charset=UTF-8")
                 .result(
diff --git a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
index e6ea3e80..12fc7277 100644
--- a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
+++ b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
@@ -1,5 +1,8 @@
 package io.javalin.plugin.openapi.dsl
 
+import io.javalin.http.ContentType.APPLICATION_JSON
+import io.javalin.http.ContentType.APPLICATION_OCTET_STREAM
+import io.javalin.http.ContentType.TEXT_PLAIN
 import io.javalin.plugin.openapi.annotations.ComposedType
 import io.javalin.plugin.openapi.annotations.ContentType
 import io.javalin.plugin.openapi.external.mediaType
@@ -104,11 +107,11 @@ fun anyOf(vararg content: DocumentedContent) = Composition.AnyOf(content.toList(
  * Try to determine the content type based on the class
  */
 fun Class<*>.guessContentType(): String =
-        when (this) {
-            String::class.java -> "text/plain"
-            ByteArray::class.java -> "application/octet-stream"
-            else -> "application/json"
-        }
+    when (this) {
+        String::class.java -> TEXT_PLAIN
+        ByteArray::class.java -> APPLICATION_OCTET_STREAM
+        else -> APPLICATION_JSON
+    }.mimeType
 
 fun Class<*>.isNonRefType(): Boolean = this in nonRefTypes
 
diff --git a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
index 8a586e11..46b97d35 100644
--- a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
+++ b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
@@ -227,7 +227,7 @@ class OpenApiDocumentation {
 
     @JvmOverloads
     fun jsonArray(status: String, returnType: Class<*>, openApiUpdater: OpenApiUpdater<ApiResponse>? = null) = apply {
-        val content = listOf(DocumentedContent(returnType, true, "application/json"))
+        val content = listOf(DocumentedContent(returnType, true, ContentType.JSON))
         val documentedResponse = DocumentedResponse(status, content)
         result(documentedResponse, openApiUpdater)
     }
@@ -240,7 +240,7 @@ class OpenApiDocumentation {
 
     @JvmOverloads
     fun json(status: String, returnType: Class<*>, openApiUpdater: OpenApiUpdater<ApiResponse>? = null) = apply {
-        val content = listOf(DocumentedContent(returnType, false, "application/json"))
+        val content = listOf(DocumentedContent(returnType, false, ContentType.JSON))
         val documentedResponse = DocumentedResponse(status, content)
         result(documentedResponse, openApiUpdater)
     }
diff --git a/javalin/src/main/java/io/javalin/Javalin.java b/javalin/src/main/java/io/javalin/Javalin.java
index 4e2ffd8f..c40e6fd8 100644
--- a/javalin/src/main/java/io/javalin/Javalin.java
+++ b/javalin/src/main/java/io/javalin/Javalin.java
@@ -59,6 +59,7 @@ public class Javalin {
     protected Javalin() {
         this.jettyServer = new JettyServer(_conf);
         this.javalinJettyServlet = new JavalinJettyServlet(_conf, javalinServlet);
+        System.out.println();
     }
 
     public Javalin(JettyServer jettyServer, JavalinJettyServlet jettyServlet) {
diff --git a/javalin/src/main/java/io/javalin/core/JavalinConfig.java b/javalin/src/main/java/io/javalin/core/JavalinConfig.java
index b793adcb..57b3efb9 100644
--- a/javalin/src/main/java/io/javalin/core/JavalinConfig.java
+++ b/javalin/src/main/java/io/javalin/core/JavalinConfig.java
@@ -22,6 +22,7 @@ import io.javalin.core.util.Header;
 import io.javalin.core.util.Headers;
 import io.javalin.core.util.HeadersPlugin;
 import io.javalin.core.util.LogUtil;
+import io.javalin.http.ContentType;
 import io.javalin.http.Handler;
 import io.javalin.http.RequestLogger;
 import io.javalin.http.SinglePageHandler;
@@ -56,7 +57,7 @@ public class JavalinConfig {
     public boolean enforceSsl = false;
     public boolean showJavalinBanner = true;
     public boolean ignoreTrailingSlashes = true;
-    @NotNull public String defaultContentType = "text/plain";
+    @NotNull public String defaultContentType = ContentType.PLAIN;
     @NotNull public String contextPath = "/";
     public Long maxRequestSize = 1_000_000L; // either increase this or use inputstream to handle large requests
     @NotNull public Long asyncRequestTimeout = 0L;
diff --git a/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt b/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
index 19e2bdaf..d46b7e3b 100644
--- a/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
+++ b/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
@@ -38,8 +38,8 @@ class RouteOverviewRenderer(val app: Javalin) : Handler {
             ]
     )
     override fun handle(ctx: Context) {
-        if (ctx.header(Header.ACCEPT)?.lowercase(Locale.ROOT)?.contains("application/json") == true) {
-            ctx.header("Content-Type", "application/json")
+        if (ctx.header(Header.ACCEPT)?.lowercase(Locale.ROOT)?.contains(ContentType.JSON) == true) {
+            ctx.header("Content-Type", ContentType.JSON)
             ctx.result(RouteOverviewUtil.createJsonOverview(handlerMetaInfoList, wsHandlerMetaInfoList))
         } else {
             ctx.html(RouteOverviewUtil.createHtmlOverview(handlerMetaInfoList, wsHandlerMetaInfoList))
diff --git a/javalin/src/main/java/io/javalin/http/ContentType.kt b/javalin/src/main/java/io/javalin/http/ContentType.kt
index 84e53c97..86e79ea7 100644
--- a/javalin/src/main/java/io/javalin/http/ContentType.kt
+++ b/javalin/src/main/java/io/javalin/http/ContentType.kt
@@ -67,6 +67,7 @@ enum class ContentType(
         /* Compile time common constants - useful for annotations & as raw string values */
 
         const val PLAIN = "text/plain"
+        const val CSS = "text/css"
         const val HTML = "text/html"
         const val XML = "text/xml"
         const val OCTET_STREAM = "application/octet-stream"
diff --git a/javalin/src/main/java/io/javalin/http/Context.kt b/javalin/src/main/java/io/javalin/http/Context.kt
index 93eec5e8..c9278cb7 100644
--- a/javalin/src/main/java/io/javalin/http/Context.kt
+++ b/javalin/src/main/java/io/javalin/http/Context.kt
@@ -436,19 +436,19 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
     }
 
     /** Sets context result to specified html string and sets content-type to text/html. */
-    fun html(html: String): Context = contentType("text/html").result(html)
+    fun html(html: String): Context = contentType(ContentType.TEXT_HTML).result(html)
 
     /**
      * Serializes object to a JSON-string using the registered [io.javalin.plugin.json.JsonMapper] and sets it as the context result.
      * Also sets content type to application/json.
      */
-    fun json(obj: Any): Context = result(jsonMapper().toJsonString(obj)).contentType("application/json")
+    fun json(obj: Any): Context = result(jsonMapper().toJsonString(obj)).contentType(ContentType.APPLICATION_JSON)
 
     /**
      * Serializes object to a JSON-stream using the registered [io.javalin.plugin.json.JsonMapper] and sets it as the context result.
      * Also sets content type to application/json.
      */
-    fun jsonStream(obj: Any): Context = result(jsonMapper().toJsonStream(obj)).contentType("application/json")
+    fun jsonStream(obj: Any): Context = result(jsonMapper().toJsonStream(obj)).contentType(ContentType.APPLICATION_JSON)
 
     /**
      * Renders a file with specified values and sets it as the context result.
diff --git a/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt b/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
index fb0de818..21725144 100644
--- a/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
+++ b/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
@@ -17,14 +17,14 @@ object HttpResponseExceptionMapper {
 
     fun handle(exception: Exception, ctx: Context) {
         val e = unwrap(exception)
-        if (ctx.header(Header.ACCEPT)?.contains("application/json") == true || ctx.res.contentType == "application/json") {
+        if (ctx.header(Header.ACCEPT)?.contains(ContentType.JSON) == true || ctx.res.contentType == ContentType.JSON) {
             ctx.status(e.status).result("""{
                 |    "title": "${e.message?.jsonEscape()}",
                 |    "status": ${e.status},
                 |    "type": "${getTypeUrl(e).lowercase(Locale.ROOT)}",
                 |    "details": {${e.details.map { """"${it.key}":"${it.value.jsonEscape()}"""" }.joinToString(",")}}
                 |}""".trimMargin()
-            ).contentType("application/json")
+            ).contentType(ContentType.APPLICATION_JSON)
         } else {
             val result = if (e.details.isEmpty()) "${e.message}" else """
                 |${e.message}
diff --git a/javalin/src/main/java/io/javalin/http/SinglePageHandler.kt b/javalin/src/main/java/io/javalin/http/SinglePageHandler.kt
index 46e5a3a8..5a13bea5 100644
--- a/javalin/src/main/java/io/javalin/http/SinglePageHandler.kt
+++ b/javalin/src/main/java/io/javalin/http/SinglePageHandler.kt
@@ -44,7 +44,7 @@ class SinglePageHandler {
 
     fun handle(ctx: Context): Boolean {
         val accept = ctx.header(Header.ACCEPT) ?: ""
-        if ("text/html" !in accept && "*/*" !in accept && accept != "") return false
+        if (ContentType.HTML !in accept && "*/*" !in accept && accept != "") return false
         pathPageMap.findByPath(ctx.path())?.let { page ->
             ctx.html(page.getHtml(reRead = ctx.isLocalhost()))
             return true
diff --git a/javalin/src/main/java/io/javalin/http/util/ContextUtil.kt b/javalin/src/main/java/io/javalin/http/util/ContextUtil.kt
index f9bfe74b..8e0c7893 100644
--- a/javalin/src/main/java/io/javalin/http/util/ContextUtil.kt
+++ b/javalin/src/main/java/io/javalin/http/util/ContextUtil.kt
@@ -9,6 +9,7 @@ package io.javalin.http.util
 import io.javalin.core.security.BasicAuthCredentials
 import io.javalin.core.util.Header
 import io.javalin.core.util.JavalinLogger
+import io.javalin.http.ContentType
 import io.javalin.http.Context
 import io.javalin.http.HandlerEntry
 import io.javalin.http.HandlerType
@@ -63,7 +64,8 @@ object ContextUtil {
         return BasicAuthCredentials(username, password)
     }
 
-    fun acceptsHtml(ctx: Context) = ctx.header(Header.ACCEPT)?.contains("text/html") == true
+    fun acceptsHtml(ctx: Context) =
+        ctx.header(Header.ACCEPT)?.contains(ContentType.HTML) == true
 
     @JvmStatic
     @JvmOverloads


Test patch:
diff --git a/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApi.kt b/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApi.kt
index 6dbe1883..d80c87ec 100644
--- a/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApi.kt
+++ b/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApi.kt
@@ -22,6 +22,7 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.Javalin
 import io.javalin.apibuilder.ApiBuilder.crud
 import io.javalin.apibuilder.CrudHandler
+import io.javalin.http.ContentType.IMAGE_PNG
 import io.javalin.http.Context
 import io.javalin.plugin.openapi.annotations.ContentType
 import io.javalin.plugin.openapi.annotations.HttpMethod
@@ -205,10 +206,10 @@ fun buildComplexExample(options: OpenApiOptions): Javalin {
             it.description = "body description"
             it.required = true
         }
-        .body<User>("application/json")
+        .body<User>(ContentType.JSON)
         .body<User>("application/xml")
         .bodyAsBytes()
-        .bodyAsBytes("image/png")
+        .bodyAsBytes(IMAGE_PNG.mimeType)
     app.put("/user", documented(putUserDocumentation) {
         val userString = it.body()
         val user = it.bodyAsClass(User::class.java)
diff --git a/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApiAnnotations.kt b/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApiAnnotations.kt
index ca283b29..e35eb3ef 100644
--- a/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApiAnnotations.kt
+++ b/javalin-openapi/src/test/java/io/javalin/plugin/openapi/TestOpenApiAnnotations.kt
@@ -23,6 +23,8 @@ import io.javalin.plugin.openapi.annotations.OpenApiSecurity
 import io.javalin.plugin.openapi.utils.VersionIssuesUtil
 import org.junit.Test
 
+private const val XML = "application/xml"
+
 // region complexExampleWithAnnotationsHandler
 @OpenApi(
         description = "Get a specific user",
@@ -35,7 +37,7 @@ import org.junit.Test
                     status = "200",
                     content = [
                         OpenApiContent(User::class),
-                        OpenApiContent(User::class, type = "application/xml")
+                        OpenApiContent(User::class, type = XML)
                     ],
                     description = "Request successful")
         ]
@@ -52,7 +54,7 @@ fun getUserHandler(ctx: Context) {
                     status = "200",
                     content = [
                         OpenApiContent(User::class),
-                        OpenApiContent(User::class, type = "application/xml")
+                        OpenApiContent(User::class, type = XML)
                     ],
                     description = "Request successful")
         ]
@@ -139,7 +141,7 @@ fun putFormDataSchemaMultipartHandler(ctx: Context) {
                 content = [
                     OpenApiContent(String::class),
                     OpenApiContent(User::class),
-                    OpenApiContent(User::class, type = "application/xml"),
+                    OpenApiContent(User::class, type = XML),
                     OpenApiContent(ByteArray::class),
                     OpenApiContent(ByteArray::class, type = "image/png")
                 ]
@@ -262,7 +264,7 @@ fun getBodyOneOfHandler(ctx: Context) {
         responses = [
             OpenApiResponse("200", content = [
                 OpenApiContent(from = Address::class),
-                OpenApiContent(from = User::class, type = "application/xml")
+                OpenApiContent(from = User::class, type = XML)
             ]),
             OpenApiResponse("200", content = [
                 OpenApiContent(from = User::class)
diff --git a/javalin-testtools/src/main/java/io/javalin/testtools/HttpClient.kt b/javalin-testtools/src/main/java/io/javalin/testtools/HttpClient.kt
index c4badf95..dcdd47bf 100644
--- a/javalin-testtools/src/main/java/io/javalin/testtools/HttpClient.kt
+++ b/javalin-testtools/src/main/java/io/javalin/testtools/HttpClient.kt
@@ -1,6 +1,7 @@
 package io.javalin.testtools
 
 import io.javalin.Javalin
+import io.javalin.http.ContentType
 import io.javalin.plugin.json.jsonMapper
 import okhttp3.MediaType.Companion.toMediaType
 import okhttp3.OkHttpClient
@@ -52,7 +53,7 @@ class HttpClient(val app: Javalin) {
     }
 
     companion object {
-        private val JSON_TYPE = "application/json".toMediaType()
+        private val JSON_TYPE = ContentType.JSON.toMediaType()
     }
 
     private fun combine(userBuilder: Consumer<Request.Builder>?, utilBuilder: Consumer<Request.Builder>): Request.Builder {
diff --git a/javalin/src/test/java/io/javalin/TestConfiguration.kt b/javalin/src/test/java/io/javalin/TestConfiguration.kt
index bfeee6d0..4ad0e741 100644
--- a/javalin/src/test/java/io/javalin/TestConfiguration.kt
+++ b/javalin/src/test/java/io/javalin/TestConfiguration.kt
@@ -9,6 +9,7 @@ package io.javalin
 import io.javalin.core.compression.CompressionStrategy
 import io.javalin.core.compression.Gzip
 import io.javalin.core.util.RouteOverviewPlugin
+import io.javalin.http.ContentType
 import io.javalin.http.staticfiles.Location
 import io.javalin.plugin.metrics.MicrometerPlugin
 import io.javalin.testing.TestUtil
@@ -34,7 +35,7 @@ class TestConfiguration {
             it.asyncRequestTimeout = 10_000L
             it.autogenerateEtags = true
             it.contextPath = "/"
-            it.defaultContentType = "text/plain"
+            it.defaultContentType = ContentType.PLAIN
             it.enableCorsForAllOrigins()
             it.enableDevLogging()
             it.registerPlugin(RouteOverviewPlugin("/test"))
diff --git a/javalin/src/test/java/io/javalin/TestEncoding.kt b/javalin/src/test/java/io/javalin/TestEncoding.kt
index 45c0c220..9a017356 100644
--- a/javalin/src/test/java/io/javalin/TestEncoding.kt
+++ b/javalin/src/test/java/io/javalin/TestEncoding.kt
@@ -9,6 +9,7 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
+import io.javalin.http.ContentType
 import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
@@ -63,28 +64,28 @@ class TestEncoding {
         app.get("/text") { ctx -> ctx.result("суп из капусты") }
         app.get("/json") { ctx -> ctx.json("白菜湯") }
         app.get("/html") { ctx -> ctx.html("kålsuppe") }
-        assertThat(http.get("/text").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("text/plain")
-        assertThat(http.get("/json").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("application/json")
-        assertThat(http.get("/html").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("text/html")
+        assertThat(http.get("/text").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.PLAIN)
+        assertThat(http.get("/json").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.JSON)
+        assertThat(http.get("/html").headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.HTML)
         assertThat(http.getBody("/text")).isEqualTo("суп из капусты")
         assertThat(http.getBody("/json")).isEqualTo("白菜湯")
         assertThat(http.getBody("/html")).isEqualTo("kålsuppe")
     }
 
     @Test
-    fun `setting a default content-type works`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
+    fun `setting a default content-type works`() = TestUtil.test(Javalin.create { it.defaultContentType = ContentType.JSON }) { app, http ->
         app.get("/default") { ctx -> ctx.result("not json") }
-        assertThat(http.get("/default").headers.getFirst(Header.CONTENT_TYPE)).contains("application/json")
+        assertThat(http.get("/default").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JSON)
     }
 
     @Test
-    fun `content-type can be overridden in handler`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
+    fun `content-type can be overridden in handler`() = TestUtil.test(Javalin.create { it.defaultContentType = ContentType.JSON }) { app, http ->
         app.get("/override") { ctx ->
             ctx.res.characterEncoding = "utf-8"
-            ctx.res.contentType = "text/html"
+            ctx.res.contentType = ContentType.HTML
         }
         assertThat(http.get("/override").headers.getFirst(Header.CONTENT_TYPE)).contains("utf-8")
-        assertThat(http.get("/override").headers.getFirst(Header.CONTENT_TYPE)).contains("text/html")
+        assertThat(http.get("/override").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.HTML)
     }
 
     @Test
diff --git a/javalin/src/test/java/io/javalin/TestFuture.kt b/javalin/src/test/java/io/javalin/TestFuture.kt
index 81112bdb..1d30a721 100644
--- a/javalin/src/test/java/io/javalin/TestFuture.kt
+++ b/javalin/src/test/java/io/javalin/TestFuture.kt
@@ -1,6 +1,7 @@
 package io.javalin
 
 import io.javalin.core.util.Header
+import io.javalin.http.ContentType
 import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
@@ -103,11 +104,11 @@ class TestFuture {
         val contentResponse = http.get("/?has-content")
         assertThat(contentResponse.status).isEqualTo(200)
         assertThat(contentResponse.body).isEqualTo("""some-future-result""")
-        assertThat(contentResponse.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("application/json")
+        assertThat(contentResponse.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.JSON)
         val noContentResponse = http.get("/?no-content")
         assertThat(noContentResponse.status).isEqualTo(204)
         assertThat(noContentResponse.body).isEqualTo(null)
-        assertThat(noContentResponse.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("text/plain")
+        assertThat(noContentResponse.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.PLAIN)
     }
 
     private val impatientServer: Javalin by lazy { Javalin.create { it.asyncRequestTimeout = 5} }
diff --git a/javalin/src/test/java/io/javalin/TestHttpResponseExceptions.kt b/javalin/src/test/java/io/javalin/TestHttpResponseExceptions.kt
index b2cb74b9..fae0d732 100644
--- a/javalin/src/test/java/io/javalin/TestHttpResponseExceptions.kt
+++ b/javalin/src/test/java/io/javalin/TestHttpResponseExceptions.kt
@@ -8,6 +8,7 @@ package io.javalin
 
 import io.javalin.core.util.Header
 import io.javalin.http.BadRequestResponse
+import io.javalin.http.ContentType
 import io.javalin.http.ForbiddenResponse
 import io.javalin.http.HttpResponseException
 import io.javalin.http.UnauthorizedResponse
@@ -38,8 +39,8 @@ class TestHttpResponseExceptions {
     @Test
     fun `response is formatted as text if client wants text`() = TestUtil.test { app, http ->
         app.post("/") { throw ForbiddenResponse() }
-        val response = http.post("/").header(Header.ACCEPT, "text/plain").asString()
-        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("text/plain")
+        val response = http.post("/").header(Header.ACCEPT, ContentType.PLAIN).asString()
+        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.PLAIN)
         assertThat(response.status).isEqualTo(HttpStatus.FORBIDDEN_403)
         assertThat(response.body).isEqualTo("Forbidden")
     }
@@ -47,8 +48,8 @@ class TestHttpResponseExceptions {
     @Test
     fun `response is formatted as json if client wants json`() = TestUtil.test { app, http ->
         app.post("/") { throw ForbiddenResponse("Off limits!") }
-        val response = http.post("/").header(Header.ACCEPT, "application/json").asString()
-        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("application/json")
+        val response = http.post("/").header(Header.ACCEPT, ContentType.JSON).asString()
+        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.JSON)
         assertThat(response.status).isEqualTo(HttpStatus.FORBIDDEN_403)
         assertThat(response.body).isEqualTo(
             """{
@@ -65,7 +66,7 @@ class TestHttpResponseExceptions {
     @Test
     fun `custom response has default type`() = TestUtil.test { app, http ->
         app.post("/") { throw CustomResponse() }
-        val response = http.post("/").header(Header.ACCEPT, "application/json").asString()
+        val response = http.post("/").header(Header.ACCEPT, ContentType.JSON).asString()
         assertThat(response.status).isEqualTo(418)
         assertThat(response.body).isEqualTo(
             """{
@@ -142,11 +143,11 @@ class TestHttpResponseExceptions {
     }
 
     @Test
-    fun `default content type affects http response errors`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
+    fun `default content type affects http response errors`() = TestUtil.test(Javalin.create { it.defaultContentType = ContentType.JSON }) { app, http ->
         app.get("/content-type") { throw ForbiddenResponse() }
         val response = http.get("/content-type")
         assertThat(response.status).isEqualTo(HttpStatus.FORBIDDEN_403)
-        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("application/json")
+        assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo(ContentType.JSON)
         assertThat(response.body).isEqualTo(
             """{
             |    "title": "Forbidden",
diff --git a/javalin/src/test/java/io/javalin/TestMultipartForms.kt b/javalin/src/test/java/io/javalin/TestMultipartForms.kt
index a56d8df4..57b9075b 100644
--- a/javalin/src/test/java/io/javalin/TestMultipartForms.kt
+++ b/javalin/src/test/java/io/javalin/TestMultipartForms.kt
@@ -6,6 +6,7 @@
 
 package io.javalin
 
+import io.javalin.http.ContentType
 import io.javalin.plugin.json.JavalinJackson
 import io.javalin.testing.TestUtil
 import io.javalin.testing.UploadInfo
@@ -57,7 +58,7 @@ class TestMultipartForms {
         val uploadInfo = JavalinJackson().fromJsonString(response.body, UploadInfo::class.java)
         assertThat(uploadInfo.size).isEqualTo(uploadFile.length())
         assertThat(uploadInfo.filename).isEqualTo(uploadFile.name)
-        assertThat(uploadInfo.contentType).isEqualTo("application/octet-stream")
+        assertThat(uploadInfo.contentType).isEqualTo(ContentType.OCTET_STREAM)
         assertThat(uploadInfo.extension).isEqualTo(".mp3")
     }
 
@@ -69,12 +70,12 @@ class TestMultipartForms {
         }
         val uploadFile = File("src/test/resources/upload-test/image.png")
         val response = http.post("/test-upload")
-                .field("upload", uploadFile, "image/png")
+                .field("upload", uploadFile, ContentType.IMAGE_PNG.mimeType)
                 .asString()
         val uploadInfo = JavalinJackson().fromJsonString(response.body, UploadInfo::class.java)
         assertThat(uploadInfo.size).isEqualTo(uploadFile.length())
         assertThat(uploadInfo.filename).isEqualTo(uploadFile.name)
-        assertThat(uploadInfo.contentType).isEqualTo("image/png")
+        assertThat(uploadInfo.contentType).isEqualTo(ContentType.IMAGE_PNG.mimeType)
         assertThat(uploadInfo.extension).isEqualTo(".png")
         assertThat(uploadInfo.size).isEqualTo(6690L)
     }
@@ -118,7 +119,7 @@ class TestMultipartForms {
         app.post("/test-upload") { ctx -> ctx.result(ctx.uploadedFiles().joinToString("\n")) }
 
         val response = http.post("/test-upload")
-                .header("content-type", "plain/text")
+                .header("content-type", ContentType.PLAIN)
                 .body("")
                 .asString()
 
@@ -199,7 +200,7 @@ class TestMultipartForms {
                         MultipartBody.Builder()
                                 .setType(MultipartBody.FORM)
                                 .addFormDataPart("prefix", prefix)
-                                .addFormDataPart("upload", tempFile.name, tempFile.asRequestBody("text/plain".toMediaTypeOrNull()))
+                                .addFormDataPart("upload", tempFile.name, tempFile.asRequestBody(ContentType.PLAIN.toMediaTypeOrNull()))
                                 .build()
                 ).build()
         ).execute().body!!.string()
diff --git a/javalin/src/test/java/io/javalin/TestResponse.kt b/javalin/src/test/java/io/javalin/TestResponse.kt
index 2bf9aa00..cc5a8edb 100644
--- a/javalin/src/test/java/io/javalin/TestResponse.kt
+++ b/javalin/src/test/java/io/javalin/TestResponse.kt
@@ -10,6 +10,7 @@ package io.javalin
 import com.mashape.unirest.http.HttpMethod
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
+import io.javalin.http.ContentType
 import io.javalin.http.util.SeekableWriter
 import io.javalin.testing.TestUtil
 import org.apache.commons.io.FileUtils
@@ -140,7 +141,7 @@ class TestResponse {
 
     @Test
     fun `seekable - range works`() = TestUtil.test { app, http ->
-        app.get("/seekable") { ctx -> ctx.seekableStream(getSeekableInput(), "text/plain") }
+        app.get("/seekable") { ctx -> ctx.seekableStream(getSeekableInput(), ContentType.PLAIN) }
         val response = Unirest.get(http.origin + "/seekable")
                 .headers(mapOf(Header.RANGE to "bytes=${SeekableWriter.chunkSize}-${SeekableWriter.chunkSize * 2 - 1}"))
                 .asString().body
@@ -149,14 +150,14 @@ class TestResponse {
 
     @Test
     fun `seekable - no-range works`() = TestUtil.test { app, http ->
-        app.get("/seekable-2") { ctx -> ctx.seekableStream(getSeekableInput(), "text/plain") }
+        app.get("/seekable-2") { ctx -> ctx.seekableStream(getSeekableInput(), ContentType.PLAIN) }
         val response = Unirest.get(http.origin + "/seekable-2").asString().body
         assertThat(response.length).isEqualTo(getSeekableInput().available())
     }
 
     @Test
     fun `seekable - overreaching range works`() = TestUtil.test { app, http ->
-        app.get("/seekable-3") { ctx -> ctx.seekableStream(getSeekableInput(), "text/plain") }
+        app.get("/seekable-3") { ctx -> ctx.seekableStream(getSeekableInput(), ContentType.PLAIN) }
         val response = Unirest.get(http.origin + "/seekable-3")
                 .headers(mapOf(Header.RANGE to "bytes=0-${SeekableWriter.chunkSize * 4}"))
                 .asString().body
@@ -165,7 +166,7 @@ class TestResponse {
 
     @Test
     fun `seekable - file smaller than chunksize works`() = TestUtil.test { app, http ->
-        app.get("/seekable-4") { ctx -> ctx.seekableStream(getSeekableInput(repeats = 50), "text/plain") }
+        app.get("/seekable-4") { ctx -> ctx.seekableStream(getSeekableInput(repeats = 50), ContentType.PLAIN) }
         val response = Unirest.get(http.origin + "/seekable-4")
                 .headers(mapOf(Header.RANGE to "bytes=0-${SeekableWriter.chunkSize}"))
                 .asString().body
diff --git a/javalin/src/test/java/io/javalin/staticfiles/TestSinglePageMode.kt b/javalin/src/test/java/io/javalin/staticfiles/TestSinglePageMode.kt
index d377f330..93344aa2 100644
--- a/javalin/src/test/java/io/javalin/staticfiles/TestSinglePageMode.kt
+++ b/javalin/src/test/java/io/javalin/staticfiles/TestSinglePageMode.kt
@@ -10,6 +10,7 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.Javalin
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
+import io.javalin.http.ContentType
 import io.javalin.http.Context
 import io.javalin.http.staticfiles.Location
 import io.javalin.testing.TestUtil
@@ -73,7 +74,7 @@ class TestSinglePageMode {
     @Test
     fun `SinglePageHandler doesn't affect static files - classpath`() = TestUtil.test(rootSinglePageApp_classPath) { _, http ->
         assertThat(http.htmlGet("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains("application/javascript")
-        assertThat(http.htmlGet("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CONTENT_TYPE)).contains("text/css")
+        assertThat(http.htmlGet("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.CSS)
         assertThat(http.htmlGet("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").status).isEqualTo(200)
     }
 
@@ -136,7 +137,7 @@ class TestSinglePageMode {
         val filePath = "src/test/external/my-special-file.html"
         val file = File(filePath).apply { createNewFile() }.apply { writeText("OLD FILE") }
         val app = Javalin.create { it.addSinglePageRoot("/", filePath, Location.EXTERNAL) }.start(0)
-        fun getSpaPage() = Unirest.get("http://localhost:${app.port()}/").header(Header.ACCEPT, "text/html").asString().body
+        fun getSpaPage() = Unirest.get("http://localhost:${app.port()}/").header(Header.ACCEPT, ContentType.HTML).asString().body
         assertThat(getSpaPage()).contains("OLD FILE")
         file.writeText("NEW FILE")
         assertThat(getSpaPage()).contains("NEW FILE")
diff --git a/javalin/src/test/java/io/javalin/staticfiles/TestStaticFiles.kt b/javalin/src/test/java/io/javalin/staticfiles/TestStaticFiles.kt
index 938c33c3..cbde68d3 100644
--- a/javalin/src/test/java/io/javalin/staticfiles/TestStaticFiles.kt
+++ b/javalin/src/test/java/io/javalin/staticfiles/TestStaticFiles.kt
@@ -10,6 +10,7 @@ package io.javalin.staticfiles
 import io.javalin.Javalin
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
+import io.javalin.http.ContentType
 import io.javalin.http.UnauthorizedResponse
 import io.javalin.http.staticfiles.Location
 import io.javalin.testing.TestLoggingUtil
@@ -133,21 +134,21 @@ class TestStaticFiles {
     @Test
     fun `serving HTML from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
         assertThat(http.get("/html.html").status).isEqualTo(200)
-        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains("text/html")
+        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.HTML)
         assertThat(http.getBody("/html.html")).contains("HTML works")
     }
 
     @Test
     fun `serving JS from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
         assertThat(http.get("/script.js").status).isEqualTo(200)
-        assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains("application/javascript")
+        assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT)
         assertThat(http.getBody("/script.js")).contains("JavaScript works")
     }
 
     @Test
     fun `serving CSS from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
         assertThat(http.get("/styles.css").status).isEqualTo(200)
-        assertThat(http.get("/styles.css").headers.getFirst(Header.CONTENT_TYPE)).contains("text/css")
+        assertThat(http.get("/styles.css").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.CSS)
         assertThat(http.getBody("/styles.css")).contains("CSS works")
     }
 
@@ -202,16 +203,16 @@ class TestStaticFiles {
     @Test
     fun `content type works in debugmmode`() = TestUtil.test(devLoggingApp) { _, http ->
         assertThat(http.get("/html.html").status).isEqualTo(200)
-        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains("text/html")
+        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.HTML)
         assertThat(http.getBody("/html.html")).contains("HTML works")
-        assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains("application/javascript")
-        assertThat(http.get("/styles.css").headers.getFirst(Header.CONTENT_TYPE)).contains("text/css")
+        assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT)
+        assertThat(http.get("/styles.css").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.CSS)
     }
 
     @Test
     fun `WebJars available if enabled`() = TestUtil.test(Javalin.create { it.enableWebjars() }) { _, http ->
         assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").status).isEqualTo(200)
-        assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CONTENT_TYPE)).contains("text/css")
+        assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.CSS)
         assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CACHE_CONTROL)).isEqualTo("max-age=31622400")
     }
 
@@ -223,7 +224,7 @@ class TestStaticFiles {
     @Test
     fun `Correct content type is returned when a custom filter with a response wrapper is added`() = TestUtil.test(customFilterStaticResourceApp) { _, http ->
         assertThat(http.get("/html.html").status).isEqualTo(200)
-        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains("text/html")
+        assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.HTML)
     }
 
     @Test
diff --git a/javalin/src/test/java/io/javalin/testing/HttpUtil.kt b/javalin/src/test/java/io/javalin/testing/HttpUtil.kt
index 90820996..8c7c3805 100644
--- a/javalin/src/test/java/io/javalin/testing/HttpUtil.kt
+++ b/javalin/src/test/java/io/javalin/testing/HttpUtil.kt
@@ -9,6 +9,7 @@ package io.javalin.testing
 import com.mashape.unirest.http.HttpMethod
 import com.mashape.unirest.http.Unirest
 import com.mashape.unirest.request.HttpRequestWithBody
+import io.javalin.plugin.openapi.annotations.ContentType
 import org.apache.http.impl.client.HttpClients
 
 class HttpUtil(port: Int) {
@@ -27,8 +28,8 @@ class HttpUtil(port: Int) {
     fun getBody(path: String, headers: Map<String, String>) = Unirest.get(origin + path).headers(headers).asString().body
     fun post(path: String) = Unirest.post(origin + path)
     fun call(method: HttpMethod, pathname: String) = HttpRequestWithBody(method, origin + pathname).asString()
-    fun htmlGet(path: String) = Unirest.get(origin + path).header("Accept", "text/html").asString()
-    fun jsonGet(path: String) = Unirest.get(origin + path).header("Accept", "application/json").asString()
+    fun htmlGet(path: String) = Unirest.get(origin + path).header("Accept", ContentType.HTML).asString()
+    fun jsonGet(path: String) = Unirest.get(origin + path).header("Accept", ContentType.JSON).asString()
     fun sse(path: String) = Unirest.get(origin + path).header("Accept", "text/event-stream").header("Connection", "keep-alive").header("Cache-Control", "no-cache").asStringAsync()
 
 }
diff --git a/javalin/src/test/kotlin/io/javalin/examples/HelloWorldImages.kt b/javalin/src/test/kotlin/io/javalin/examples/HelloWorldImages.kt
index 44ae1bd0..4647e06b 100644
--- a/javalin/src/test/kotlin/io/javalin/examples/HelloWorldImages.kt
+++ b/javalin/src/test/kotlin/io/javalin/examples/HelloWorldImages.kt
@@ -8,6 +8,7 @@ package io.javalin.examples
 
 import io.javalin.Javalin
 import io.javalin.core.util.JavalinLogger
+import io.javalin.http.ContentType
 import java.awt.image.BufferedImage
 import java.io.ByteArrayOutputStream
 import javax.imageio.ImageIO
@@ -32,7 +33,7 @@ fun main() {
         val success = ImageIO.write(image, "png", os)
         val bytes = os.toByteArray()
 
-        ctx.contentType("image/png").result(bytes)
+        ctx.contentType(ContentType.IMAGE_PNG).result(bytes)
     }
 
     JavalinLogger.info("Red square: http://localhost:7070/image/ff0000/width/400/height/400")