From 05776e259f89b3ca079815d7c2c3df942150bf13 Mon Sep 17 00:00:00 2001 From: mytang0 Date: Mon, 20 Feb 2023 17:21:02 +0800 Subject: [PATCH] Support exception mapper extension --- .../protocol/rest/BaseRestProtocolServer.java | 5 ++-- .../dubbo/rpc/protocol/rest/Constants.java | 3 ++ .../rpc/protocol/rest/RpcExceptionMapper.java | 9 ++---- .../rpc/protocol/rest/RestProtocolTest.java | 30 +++++++++++++++++-- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java index 1996d40e813..baa0fff9cc2 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; +import static org.apache.dubbo.rpc.protocol.rest.Constants.EXCEPTION_MAPPER_KEY; import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY; public abstract class BaseRestProtocolServer implements RestProtocolServer { @@ -38,8 +39,8 @@ public void start(URL url) { getDeployment().getMediaTypeMappings().put("json", "application/json"); getDeployment().getMediaTypeMappings().put("xml", "text/xml"); getDeployment().getProviderClasses().add(RpcContextFilter.class.getName()); - // TODO users can override this mapper, but we just rely on the current priority strategy of resteasy - getDeployment().getProviderClasses().add(RpcExceptionMapper.class.getName()); + + loadProviders(url.getParameter(EXCEPTION_MAPPER_KEY, RpcExceptionMapper.class.getName())); loadProviders(url.getParameter(EXTENSION_KEY, "")); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java index e9066131b59..8966315425c 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java @@ -35,4 +35,7 @@ public interface Constants { String TOMCAT = "tomcat"; String NETTY = "netty"; + + // exception mapper + String EXCEPTION_MAPPER_KEY = "exception.mapper"; } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java index 005e4369a3f..de6d2e45a6d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java @@ -28,12 +28,10 @@ public class RpcExceptionMapper implements ExceptionMapper { @Override public Response toResponse(RpcException e) { - // TODO do more sophisticated exception handling and output if (e.getCause() instanceof ConstraintViolationException) { return handleConstraintViolationException((ConstraintViolationException) e.getCause()); } // we may want to avoid exposing the dubbo exception details to certain clients - // TODO for now just do plain text output return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal server error: " + e.getMessage()).type(ContentType.TEXT_PLAIN_UTF_8).build(); } @@ -41,11 +39,10 @@ protected Response handleConstraintViolationException(ConstraintViolationExcepti ViolationReport report = new ViolationReport(); for (ConstraintViolation cv : cve.getConstraintViolations()) { report.addConstraintViolation(new RestConstraintViolation( - cv.getPropertyPath().toString(), - cv.getMessage(), - cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); + cv.getPropertyPath().toString(), + cv.getMessage(), + cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); } - // TODO for now just do xml output return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.TEXT_XML_UTF_8).build(); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java index 758c7fb9002..b0b805682ca 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java @@ -38,9 +38,12 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; import java.util.Map; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; +import static org.apache.dubbo.rpc.protocol.rest.Constants.EXCEPTION_MAPPER_KEY; import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -48,8 +51,8 @@ import static org.hamcrest.MatcherAssert.assertThat; class RestProtocolTest { - private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest"); - private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + private final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest"); + private final ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); private final int availablePort = NetUtils.getAvailablePort(); private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); @@ -246,6 +249,29 @@ void testDefaultPort() { assertThat(protocol.getDefaultPort(), is(80)); } + @Test + void testExceptionMapper() { + DemoService server = new DemoServiceImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL exceptionUrl = url.addParameter(EXCEPTION_MAPPER_KEY, TestExceptionMapper.class.getName()); + + protocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl)); + + DemoService referDemoService = this.proxy.getProxy(protocol.refer(DemoService.class, exceptionUrl)); + + Assertions.assertEquals("test-exception", referDemoService.error()); + } + + public static class TestExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(RuntimeException e) { + return Response.ok("test-exception").build(); + } + } + private URL registerProvider(URL url, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); ProviderModel providerModel = new ProviderModel(