This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit e7c87cb0544049fec94d9495fcadd64d3c4d6556
Author: Dimitris Soumis <[email protected]>
AuthorDate: Tue Jun 9 14:08:10 2026 +0300

    Add showReport attribute in JsonErrorReportValve.
---
 .../catalina/valves/JsonErrorReportValve.java      | 124 +++++++++++----------
 .../catalina/valves/TestJsonErrorReportValve.java  |  74 ++++++++++++
 webapps/docs/config/valve.xml                      |   9 ++
 3 files changed, 149 insertions(+), 58 deletions(-)

diff --git a/java/org/apache/catalina/valves/JsonErrorReportValve.java 
b/java/org/apache/catalina/valves/JsonErrorReportValve.java
index a5b0a58747..cf6622d907 100644
--- a/java/org/apache/catalina/valves/JsonErrorReportValve.java
+++ b/java/org/apache/catalina/valves/JsonErrorReportValve.java
@@ -61,73 +61,81 @@ public class JsonErrorReportValve extends ErrorReportValve {
             return;
         }
 
-        StringManager smClient = StringManager.getManager(Constants.Package, 
request.getLocales());
-        response.setLocale(smClient.getLocale());
-        String type;
-        if (throwable != null) {
-            type = smClient.getString("errorReportValve.exceptionReport");
-        } else {
-            type = smClient.getString("errorReportValve.statusReport");
-        }
-        String message = response.getMessage();
-        if (message == null && throwable != null) {
-            message = throwable.getMessage();
-        }
-        if (message == null) {
-            message = "";
-        }
-        String description = smClient.getString("http." + statusCode + 
".desc");
-        if (description == null) {
-            if (message.isEmpty()) {
-                return;
+        StringBuilder sb = new StringBuilder();
+        sb.append("{\n");
+        sb.append("  \"status\": ").append(statusCode);
+
+        if (isShowReport()) {
+            StringManager smClient = 
StringManager.getManager(Constants.Package, request.getLocales());
+            response.setLocale(smClient.getLocale());
+            String type;
+            if (throwable != null) {
+                type = smClient.getString("errorReportValve.exceptionReport");
             } else {
-                description = 
smClient.getString("errorReportValve.noDescription");
+                type = smClient.getString("errorReportValve.statusReport");
             }
-        }
-        StringBuilder sb = new StringBuilder();
-        sb.append("{\n  \"type\": 
\"").append(JSONFilter.escape(type)).append("\",\n");
-        sb.append("  \"status\": ").append(statusCode).append(",\n");
-        sb.append("  \"message\": 
\"").append(JSONFilter.escape(message)).append("\",\n");
-        sb.append("  \"description\": 
\"").append(JSONFilter.escape(description));
-
-        if (throwable != null) {
-            sb.append("\",\n");
-
-            // Stack trace
-            sb.append("  \"throwable\": [");
-            int loops = 0;
-            boolean first = true;
-            do {
-                if (!first) {
-                    sb.append(',');
+            String message = response.getMessage();
+            if (message == null && throwable != null) {
+                message = throwable.getMessage();
+            }
+            if (message == null) {
+                message = "";
+            }
+            String description = smClient.getString("http." + statusCode + 
".desc");
+            if (description == null) {
+                if (message.isEmpty()) {
+                    return;
                 } else {
-                    first = false;
+                    description = 
smClient.getString("errorReportValve.noDescription");
                 }
-                
sb.append('\"').append(JSONFilter.escape(throwable.toString())).append('\"');
-
-                StackTraceElement[] elements = throwable.getStackTrace();
-                int pos = elements.length;
-                for (int i = elements.length - 1; i >= 0; i--) {
-                    if 
((elements[i].getClassName().startsWith("org.apache.catalina.core.ApplicationFilterChain"))
 &&
-                            
(elements[i].getMethodName().equals("internalDoFilter"))) {
-                        pos = i;
-                        break;
+            }
+            sb.append(",\n");
+            sb.append("  \"type\": 
\"").append(JSONFilter.escape(type)).append("\",\n");
+            sb.append("  \"message\": 
\"").append(JSONFilter.escape(message)).append("\",\n");
+            sb.append("  \"description\": 
\"").append(JSONFilter.escape(description));
+
+            if (throwable != null) {
+                sb.append("\",\n");
+
+                // Stack trace
+                sb.append("  \"throwable\": [");
+                int loops = 0;
+                boolean first = true;
+                do {
+                    if (!first) {
+                        sb.append(',');
+                    } else {
+                        first = false;
                     }
-                }
-                for (int i = 0; i < pos; i++) {
-                    if 
(!(elements[i].getClassName().startsWith("org.apache.catalina.core."))) {
-                        sb.append(',').append('\"').append(' 
').append(JSONFilter.escape(elements[i].toString()))
-                                .append('\"');
+                    
sb.append('\"').append(JSONFilter.escape(throwable.toString())).append('\"');
+
+                    StackTraceElement[] elements = throwable.getStackTrace();
+                    int pos = elements.length;
+                    for (int i = elements.length - 1; i >= 0; i--) {
+                        if (elements[i].getClassName()
+                                
.startsWith("org.apache.catalina.core.ApplicationFilterChain") &&
+                                
elements[i].getMethodName().equals("internalDoFilter")) {
+                            pos = i;
+                            break;
+                        }
+                    }
+                    for (int i = 0; i < pos; i++) {
+                        if 
(!elements[i].getClassName().startsWith("org.apache.catalina.core.")) {
+                            sb.append(',').append('\"').append(' ')
+                                    
.append(JSONFilter.escape(elements[i].toString())).append('\"');
+                        }
                     }
-                }
 
-                throwable = throwable.getCause();
-                loops++;
-            } while (throwable != null && (loops < 10));
-            sb.append("]\n}");
+                    throwable = throwable.getCause();
+                    loops++;
+                } while (throwable != null && loops < 10);
+                sb.append("]\n}");
 
+            } else {
+                sb.append("\"\n}");
+            }
         } else {
-            sb.append("\"\n}");
+            sb.append("\n}");
         }
 
         try {
diff --git a/test/org/apache/catalina/valves/TestJsonErrorReportValve.java 
b/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
index 1d66112e36..95d1369721 100644
--- a/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
+++ b/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
@@ -34,6 +34,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.Valve;
 import org.apache.catalina.core.StandardHost;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
@@ -338,6 +339,79 @@ public class TestJsonErrorReportValve extends 
TomcatBaseTest {
         Assert.assertEquals(unicodeMessage, json.get("message"));
     }
 
+    @Test
+    public void testJsonErrorShowReportFalse() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+        StandardHost host = (StandardHost) tomcat.getHost();
+        host.setErrorReportValveClass(JSON_VALVE);
+
+        Context ctx = getProgrammaticRootContext();
+
+        Tomcat.addServlet(ctx, "sendError", new SendErrorServlet(
+                HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server broke"));
+        ctx.addServletMappingDecoded("/", "sendError");
+
+        tomcat.start();
+
+        for (Valve valve : host.getPipeline().getValves()) {
+            if (valve instanceof JsonErrorReportValve) {
+                ((JsonErrorReportValve) valve).setShowReport(false);
+                break;
+            }
+        }
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:"; + getPort(), res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+        String body = res.toString();
+        JSONParser parser = new JSONParser(body);
+        LinkedHashMap<String, Object> json = parser.parseObject();
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
((Number) json.get("status")).intValue());
+        Assert.assertNull(json.get("type"));
+        Assert.assertNull(json.get("message"));
+        Assert.assertNull(json.get("description"));
+    }
+
+
+    @Test
+    public void testJsonErrorWithThrowableShowReportFalse() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+        StandardHost host = (StandardHost) tomcat.getHost();
+        host.setErrorReportValveClass(JSON_VALVE);
+
+        Context ctx = getProgrammaticRootContext();
+
+        Tomcat.addServlet(ctx, "exception", new ExceptionServlet("Something 
went wrong"));
+        ctx.addServletMappingDecoded("/", "exception");
+
+        tomcat.start();
+
+        for (Valve valve : host.getPipeline().getValves()) {
+            if (valve instanceof JsonErrorReportValve) {
+                ((JsonErrorReportValve) valve).setShowReport(false);
+                break;
+            }
+        }
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:"; + getPort(), res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+        String body = res.toString();
+        JSONParser parser = new JSONParser(body);
+        LinkedHashMap<String, Object> json = parser.parseObject();
+
+        Assert.assertEquals(500, ((Number) json.get("status")).intValue());
+        Assert.assertNull(json.get("throwable"));
+        Assert.assertNull(json.get("message"));
+        Assert.assertNull(json.get("description"));
+    }
+
+
     @Test
     public void testNoJsonForUnknownStatusWithoutMessage() throws Exception {
         Tomcat tomcat = getTomcatInstance();
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index cce96e05f8..f80e14c862 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -2444,6 +2444,15 @@
         <strong>org.apache.catalina.valves.JsonErrorReportValve</strong>.</p>
       </attribute>
 
+      <attribute name="showReport" required="false">
+        <p>Flag to determine if the error report (custom error message,
+           description and/or stack trace) is included in the JSON response
+           when an error occurs. If set to <code>false</code>, then the JSON
+           response will only contain the HTTP status code.
+           Default value: <code>true</code>
+        </p>
+      </attribute>
+
     </attributes>
 
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to