diff --git a/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java b/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java
index a9de0e1b3..1120cd48e 100644
--- a/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java
+++ b/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java
@@ -41,5 +41,5 @@
/**
* The response status code to use.
*/
- int statusCode() default 0;
+ HttpStatus statusCode() default HttpStatus.__not_set;
}
diff --git a/http-api/src/main/java/io/avaje/http/api/HttpStatus.java b/http-api/src/main/java/io/avaje/http/api/HttpStatus.java
new file mode 100644
index 000000000..6591fd99a
--- /dev/null
+++ b/http-api/src/main/java/io/avaje/http/api/HttpStatus.java
@@ -0,0 +1,84 @@
+package io.avaje.http.api;
+
+public enum HttpStatus {
+ CONTINUE_100(100),
+ SWITCHING_PROTOCOLS_101(101),
+ PROCESSING_102(102),
+
+ OK_200(200),
+ CREATED_201(201),
+ ACCEPTED_202(202),
+ NON_AUTHORITATIVE_INFORMATION_203(203),
+
+ NO_CONTENT_204(204),
+ RESET_CONTENT_205(205),
+ PARTIAL_CONTENT_206(206),
+ MULTI_STATUS_207(207),
+ ALREADY_REPORTED_208(208),
+ IM_USED_226(226),
+
+ MULTIPLE_CHOICES_300(300),
+ MOVED_PERMANENTLY_301(301),
+ FOUND_302(302),
+ SEE_OTHER_303(303),
+ NOT_MODIFIED_304(304),
+ USE_PROXY_305(305),
+ TEMPORARY_REDIRECT_307(307),
+ PERMANENT_REDIRECT_308(308),
+
+ BAD_REQUEST_400(400),
+ UNAUTHORIZED_401(401),
+ PAYMENT_REQUIRED_402(402),
+ FORBIDDEN_403(403),
+ NOT_FOUND_404(404),
+ METHOD_NOT_ALLOWED_405(405),
+ NOT_ACCEPTABLE_406(406),
+ PROXY_AUTHENTICATION_REQUIRED_407(407),
+ REQUEST_TIMEOUT_408(408),
+ CONFLICT_409(409),
+ GONE_410(410),
+ LENGTH_REQUIRED_411(411),
+ PRECONDITION_FAILED_412(412),
+ REQUEST_ENTITY_TOO_LARGE_413(413),
+ URI_TOO_LONG_414(414),
+ UNSUPPORTED_MEDIA_TYPE_415(415),
+ REQUESTED_RANGE_NOT_SATISFIABLE_416(416),
+ EXPECTATION_FAILED_417(417),
+ I_AM_A_TEAPOT_418(418),
+ MISDIRECTED_REQUEST_421(421),
+ UNPROCESSABLE_ENTITY_422(422),
+ LOCKED_423(423),
+ FAILED_DEPENDENCY_424(424),
+ TOO_EARLY_425(425),
+ UPGRADE_REQUIRED_426(426),
+ PRECONDITION_REQUIRED_428(428),
+ TOO_MANY_REQUESTS_429(429),
+ REQUEST_HEADER_FIELDS_TOO_LARGE_431(431),
+ UNAVAILABLE_FOR_LEGAL_REASONS_451(451),
+
+ INTERNAL_SERVER_ERROR_500(500),
+ NOT_IMPLEMENTED_501(501),
+ BAD_GATEWAY_502(502),
+ SERVICE_UNAVAILABLE_503(503),
+ GATEWAY_TIMEOUT_504(504),
+ HTTP_VERSION_NOT_SUPPORTED_505(505),
+ VARIANT_ALSO_NEGOTIATES_506(506),
+ INSUFFICIENT_STORAGE(507),
+ LOOP_DETECTED(508),
+ BANDWIDTH_LIMIT_EXCEEDED_509(509),
+ NOT_EXTENDED_510(510),
+ NETWORK_AUTHENTICATION_REQUIRED(511),
+
+ __not_set(0);
+
+ private final int code;
+
+ HttpStatus(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return code;
+ }
+
+}
diff --git a/http-api/src/main/java/io/avaje/http/api/Produces.java b/http-api/src/main/java/io/avaje/http/api/Produces.java
index 09b4db9a6..6055579df 100644
--- a/http-api/src/main/java/io/avaje/http/api/Produces.java
+++ b/http-api/src/main/java/io/avaje/http/api/Produces.java
@@ -45,5 +45,5 @@
* PATCH(200, void methods 204)
* DELETE(200, void methods 204)
*/
- int statusCode() default 0;
+ HttpStatus statusCode() default HttpStatus.__not_set;
}
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java
new file mode 100644
index 000000000..0f64549a5
--- /dev/null
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java
@@ -0,0 +1,83 @@
+package io.avaje.http.generator.core;
+
+public enum CopyHttpStatus {
+ CONTINUE_100(100),
+ SWITCHING_PROTOCOLS_101(101),
+ PROCESSING_102(102),
+
+ OK_200(200),
+ CREATED_201(201),
+ ACCEPTED_202(202),
+ NON_AUTHORITATIVE_INFORMATION_203(203),
+
+ NO_CONTENT_204(204),
+ RESET_CONTENT_205(205),
+ PARTIAL_CONTENT_206(206),
+ MULTI_STATUS_207(207),
+ ALREADY_REPORTED_208(208),
+ IM_USED_226(226),
+
+ MULTIPLE_CHOICES_300(300),
+ MOVED_PERMANENTLY_301(301),
+ FOUND_302(302),
+ SEE_OTHER_303(303),
+ NOT_MODIFIED_304(304),
+ USE_PROXY_305(305),
+ TEMPORARY_REDIRECT_307(307),
+ PERMANENT_REDIRECT_308(308),
+
+ BAD_REQUEST_400(400),
+ UNAUTHORIZED_401(401),
+ PAYMENT_REQUIRED_402(402),
+ FORBIDDEN_403(403),
+ NOT_FOUND_404(404),
+ METHOD_NOT_ALLOWED_405(405),
+ NOT_ACCEPTABLE_406(406),
+ PROXY_AUTHENTICATION_REQUIRED_407(407),
+ REQUEST_TIMEOUT_408(408),
+ CONFLICT_409(409),
+ GONE_410(410),
+ LENGTH_REQUIRED_411(411),
+ PRECONDITION_FAILED_412(412),
+ REQUEST_ENTITY_TOO_LARGE_413(413),
+ URI_TOO_LONG_414(414),
+ UNSUPPORTED_MEDIA_TYPE_415(415),
+ REQUESTED_RANGE_NOT_SATISFIABLE_416(416),
+ EXPECTATION_FAILED_417(417),
+ I_AM_A_TEAPOT_418(418),
+ MISDIRECTED_REQUEST_421(421),
+ UNPROCESSABLE_ENTITY_422(422),
+ LOCKED_423(423),
+ FAILED_DEPENDENCY_424(424),
+ TOO_EARLY_425(425),
+ UPGRADE_REQUIRED_426(426),
+ PRECONDITION_REQUIRED_428(428),
+ TOO_MANY_REQUESTS_429(429),
+ REQUEST_HEADER_FIELDS_TOO_LARGE_431(431),
+ UNAVAILABLE_FOR_LEGAL_REASONS_451(451),
+
+ INTERNAL_SERVER_ERROR_500(500),
+ NOT_IMPLEMENTED_501(501),
+ BAD_GATEWAY_502(502),
+ SERVICE_UNAVAILABLE_503(503),
+ GATEWAY_TIMEOUT_504(504),
+ HTTP_VERSION_NOT_SUPPORTED_505(505),
+ VARIANT_ALSO_NEGOTIATES_506(506),
+ INSUFFICIENT_STORAGE(507),
+ LOOP_DETECTED(508),
+ BANDWIDTH_LIMIT_EXCEEDED_509(509),
+ NOT_EXTENDED_510(510),
+ NETWORK_AUTHENTICATION_REQUIRED(511),
+
+ __not_set(0);
+
+ private final int code;
+
+ CopyHttpStatus(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return code;
+ }
+}
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java
index 26ae68fc3..7f049336c 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java
@@ -48,7 +48,7 @@ public class MethodReader {
private final Optional timeout;
private WebMethod webMethod;
- private int statusCode;
+ private CopyHttpStatus statusCode = CopyHttpStatus.__not_set;
private String webMethodPath;
private boolean formMarker;
private final boolean instrumentContext;
@@ -169,7 +169,7 @@ private void initSetWebMethod(WebMethod webMethod, String value) {
private void initSetWebMethod(WebMethod webMethod, ExceptionHandlerPrism exceptionPrism) {
this.webMethod = webMethod;
- this.statusCode = exceptionPrism.statusCode();
+ this.statusCode = CopyHttpStatus.valueOf(exceptionPrism.statusCode());
var exType = exceptionPrism.value().toString();
if ("io.avaje.http.api.DefaultException".equals(exType)) {
exType =
@@ -352,7 +352,11 @@ public boolean isVoid() {
}
public boolean hasProducesStatus() {
- return producesAnnotation.map(ProducesPrism::statusCode).filter(s -> s > 0).isPresent();
+ return producesAnnotation
+ .map(ProducesPrism::statusCode)
+ .map(CopyHttpStatus::valueOf)
+ .filter(s -> s.code() > 0)
+ .isPresent();
}
public String produces() {
@@ -379,12 +383,14 @@ public TypeMirror returnType() {
}
public int statusCode() {
- if (statusCode > 0) {
+ if (statusCode.code() > 0) {
// using explicit status code
- return statusCode;
+ return statusCode.code();
}
return producesAnnotation
.map(ProducesPrism::statusCode)
+ .map(CopyHttpStatus::valueOf)
+ .map(CopyHttpStatus::code)
.filter(s -> s > 0)
.orElseGet(() -> webMethod.statusCode(isVoid));
}
diff --git a/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java b/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java
index 709e76b43..b9a0ff8f7 100644
--- a/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java
+++ b/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java
@@ -2,15 +2,7 @@
import java.util.List;
-import io.avaje.http.api.Controller;
-import io.avaje.http.api.Get;
-import io.avaje.http.api.MediaType;
-import io.avaje.http.api.OpenAPIResponse;
-import io.avaje.http.api.OpenAPIResponses;
-import io.avaje.http.api.Path;
-import io.avaje.http.api.Post;
-import io.avaje.http.api.Produces;
-import io.avaje.http.api.Put;
+import io.avaje.http.api.*;
import io.javalin.http.Context;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
@@ -84,7 +76,7 @@ Person testPostList(List m) {
}
@Put("/put")
- @Produces(value = MediaType.TEXT_PLAIN, statusCode = 203)
+ @Produces(value = MediaType.TEXT_PLAIN, statusCode = HttpStatus.NON_AUTHORITATIVE_INFORMATION_203)
@OpenAPIResponse(responseCode = "204", type = String.class)
String testDefaultStatus(Context ctx) {
if (ctx.contentType().equals(MediaType.APPLICATION_PDF)) {
diff --git a/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java b/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java
index bf841810b..37ca51616 100644
--- a/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java
+++ b/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java
@@ -2,6 +2,7 @@
import io.avaje.http.api.Controller;
import io.avaje.http.api.ExceptionHandler;
+import io.avaje.http.api.HttpStatus;
import io.helidon.nima.webserver.http.ServerRequest;
import io.helidon.nima.webserver.http.ServerResponse;
@@ -13,7 +14,7 @@
@Controller
final class ErrorController {
- @ExceptionHandler(statusCode = 407)
+ @ExceptionHandler(statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407)
Map runEx(RuntimeException ex, ServerRequest req, ServerResponse res) {
return Map.of("err", "" + ex);
}
diff --git a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
index 6b2412921..c17daf6a0 100644
--- a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
+++ b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
@@ -69,7 +69,7 @@ String strBody(@BodyString String body) {
return body;
}
- @Produces(statusCode = 202)
+ @Produces(statusCode = HttpStatus.ACCEPTED_202)
@Post("/blah")
Map strBody2() {
var map = new LinkedHashMap();
@@ -83,7 +83,7 @@ String exception(IllegalArgumentException ex) {
return "Err: " + ex;
}
- @Produces(statusCode = 501)
+ @Produces(statusCode = HttpStatus.NOT_IMPLEMENTED_501)
@ExceptionHandler
Person exceptionCtx(Exception ex, ServerRequest req, ServerResponse res) {
res.header("X-Foo", "WasHere");