diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java index 12d0644ac8..f4d57b1c30 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java @@ -31,6 +31,9 @@ public class PathMatcher { private String[] pathSplits; private boolean hasPathVariable; private String contextPath; + private String httpMethod; + // for provider http method compare + private boolean needCompareMethod = true; public PathMatcher(String path) { @@ -45,6 +48,10 @@ public class PathMatcher { this.port = (port == null || port == -1 || port == 0) ? null : port; } + public PathMatcher(String path, String version, String group, Integer port, String httpMethod) { + this(path, version, group, port); + setHttpMethod(httpMethod); + } private void dealPathVariable(String path) { this.pathSplits = path.split(SEPARATOR); @@ -88,8 +95,8 @@ public class PathMatcher { } - public static PathMatcher getInvokeCreatePathMatcher(String path, String version, String group, Integer port) { - return new PathMatcher(path, version, group, port); + public static PathMatcher getInvokeCreatePathMatcher(String path, String version, String group, Integer port, String method) { + return new PathMatcher(path, version, group, port, method).noNeedHttpMethodCompare(); } public boolean hasPathVariable() { @@ -100,6 +107,20 @@ public class PathMatcher { return port; } + public String getHttpMethod() { + return httpMethod; + } + + public PathMatcher setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + return this; + } + + private PathMatcher noNeedHttpMethodCompare() { + this.needCompareMethod = false; + return this; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -107,6 +128,7 @@ public class PathMatcher { PathMatcher that = (PathMatcher) o; return pathEqual(that) && Objects.equals(version, that.version) + && (this.needCompareMethod ? Objects.equals(httpMethod, that.httpMethod) : true) && Objects.equals(group, that.group) && Objects.equals(port, that.port); } @@ -200,6 +222,7 @@ public class PathMatcher { ", port=" + port + ", hasPathVariable=" + hasPathVariable + ", contextPath='" + contextPath + '\'' + + ", httpMethod='" + httpMethod + '\'' + '}'; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java index 66ff17f5ad..e7f825d26a 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java @@ -206,6 +206,10 @@ public class RequestMetadata implements Serializable { setPath(contextPathFromUrl + path); } + public boolean methodAllowed(String method) { + return method != null && method.equals(this.method); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java index 55970c7fe7..14f8bdcbb9 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java @@ -110,7 +110,7 @@ public class ServiceRestMetadata implements Serializable { public void addRestMethodMetadata(RestMethodMetadata restMethodMetadata) { PathMatcher pathMather = new PathMatcher(restMethodMetadata.getRequest().getPath(), - this.getVersion(), this.getGroup(), this.getPort()); + this.getVersion(), this.getGroup(), this.getPort(),restMethodMetadata.getRequest().getMethod()); addPathToServiceMap(pathMather, restMethodMetadata); addMethodToServiceMap(restMethodMetadata); getMeta().add(restMethodMetadata); diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsUsingService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsUsingService.java new file mode 100644 index 0000000000..7490c75341 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsUsingService.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.api; + + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("usingService") +@Consumes({MediaType.APPLICATION_JSON}) +@Produces({MediaType.APPLICATION_JSON}) +public interface JaxrsUsingService { + + @GET + Response getUsers(); + + @POST + Response createUser(Object user); + + @GET + @Path("{uid}") + Response getUserByUid(@PathParam("uid") String uid); + + @DELETE + @Path("{uid}") + Response deleteUserByUid(@PathParam("uid") String uid); +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java index 97ce07d5ec..3e8f129f20 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java @@ -16,13 +16,18 @@ */ package org.apache.dubbo.metadata.rest.jaxrs; +import org.apache.dubbo.metadata.rest.PathMatcher; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.metadata.rest.api.JaxrsRestDoubleCheckContainsPathVariableService; import org.apache.dubbo.metadata.rest.api.JaxrsRestDoubleCheckService; +import org.apache.dubbo.metadata.rest.api.JaxrsUsingService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Map; + public class JaxrsRestDoubleCheckTest { private JAXRSServiceRestMetadataResolver instance = new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel()); @@ -45,4 +50,27 @@ public class JaxrsRestDoubleCheckTest { } + @Test + void testSameHttpMethodException() { + + Assertions.assertDoesNotThrow(() -> { + ServiceRestMetadata resolve = new ServiceRestMetadata(); + resolve.setServiceInterface(JaxrsUsingService.class.getName()); + instance.resolve(JaxrsUsingService.class, resolve); + }); + + ServiceRestMetadata resolve = new ServiceRestMetadata(); + resolve.setServiceInterface(JaxrsUsingService.class.getName()); + instance.resolve(JaxrsUsingService.class, resolve); + + Map pathContainPathVariableToServiceMap = resolve.getPathContainPathVariableToServiceMap(); + + + RestMethodMetadata restMethodMetadata = pathContainPathVariableToServiceMap.get(PathMatcher.getInvokeCreatePathMatcher("/usingService/aaa", null, null, null, "TEST")); + + Assertions.assertNotNull(restMethodMetadata); + + + } + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java index 1d70dfb839..1d7aa3de05 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java @@ -65,10 +65,10 @@ public class PathAndInvokerMapper { * @param port * @return */ - public InvokerAndRestMethodMetadataPair getRestMethodMetadata(String path, String version, String group, Integer port) { + public InvokerAndRestMethodMetadataPair getRestMethodMetadata(String path, String version, String group, Integer port,String method) { - PathMatcher pathMather = PathMatcher.getInvokeCreatePathMatcher(path, version, group, port); + PathMatcher pathMather = PathMatcher.getInvokeCreatePathMatcher(path, version, group, port,method); // first search from pathToServiceMapNoPathVariable if (pathToServiceMapNoPathVariable.containsKey(pathMather)) { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java index 8388e1fe88..1d0221a587 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java @@ -134,8 +134,9 @@ public class RestRPCInvocationUtil { String path = request.getPath(); String version = request.getHeader(RestHeaderEnum.VERSION.getHeader()); String group = request.getHeader(RestHeaderEnum.GROUP.getHeader()); + String method = request.getMethod(); - return pathAndInvokerMapper.getRestMethodMetadata(path, version, group, null); + return pathAndInvokerMapper.getRestMethodMetadata(path, version, group, null, method); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java index a3243685ab..7edf7c4ddf 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java @@ -104,6 +104,16 @@ public class NettyHttpHandler implements HttpHandler returnType) { + private MediaType getAcceptMediaType(RequestFacade request, Class returnType) { String accept = request.getHeader(RestHeaderEnum.ACCEPT.getHeader()); MediaType mediaType = MediaTypeUtil.convertMediaType(returnType, accept); return mediaType; @@ -176,7 +186,7 @@ public class NettyHttpHandler implements HttpHandler returnType) { try { // media type judge - getAcceptMediaType(requestFacade,returnType); + getAcceptMediaType(requestFacade, returnType); } catch (UnSupportContentTypeException e) { // return type judge MediaType mediaType = HttpMessageCodecManager.typeSupport(returnType); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java index deca0a6937..c227ba68f2 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java @@ -46,8 +46,9 @@ import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService; import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl; import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodService; import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodServiceImpl; - import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException; +import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService; +import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoServiceImpl; import org.hamcrest.CoreMatchers; import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.junit.jupiter.api.AfterEach; @@ -60,7 +61,6 @@ import java.util.HashMap; 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; @@ -561,6 +561,40 @@ class JaxrsRestProtocolTest { } } + @Test + void test405() { + int availablePort = NetUtils.getAvailablePort(); + URL url = URL.valueOf("rest://127.0.0.1:" + availablePort + + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService&" + ); + + RestDemoServiceImpl server = new RestDemoServiceImpl(); + + url = this.registerProvider(url, server, RestDemoService.class); + + Exporter exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, url)); + + URL consumer = URL.valueOf("rest://127.0.0.1:" + availablePort + + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException&" + ); + + consumer = this.registerProvider(consumer, server, RestDemoForTestException.class); + + Invoker invoker = protocol.refer(RestDemoForTestException.class, consumer); + + + RestDemoForTestException client = proxy.getProxy(invoker); + + Assertions.assertThrows(RpcException.class, () -> { + client.testMethodDisallowed("aaa"); + + }); + + + invoker.destroy(); + exporter.unexport(); + } + private URL registerProvider(URL url, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); ProviderModel providerModel = new ProviderModel( diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java index 718b665ce6..c6c36593f4 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java @@ -20,6 +20,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @@ -36,4 +37,8 @@ public interface RestDemoForTestException { @Consumes({MediaType.TEXT_PLAIN}) @Path("/hello") Integer test400(@QueryParam("a")String a,@QueryParam("b") String b); + + @POST + @Path("{uid}") + String testMethodDisallowed(@PathParam("uid") String uid); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java index 4f04bd3894..c423bbf3cf 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java @@ -42,4 +42,8 @@ public interface RestDemoService { Long testFormBody(@FormParam("number") Long number); boolean isCalled(); + + @DELETE + @Path("{uid}") + String deleteUserByUid(@PathParam("uid") String uid); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java index bed70b7657..3ba0858b23 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java @@ -42,6 +42,11 @@ public class RestDemoServiceImpl implements RestDemoService { return called; } + @Override + public String deleteUserByUid(String uid) { + return uid; + } + @Override public Integer hello(Integer a, Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments();