From ca761ae2b3029945d8aa6d1973c7f91ca855647f Mon Sep 17 00:00:00 2001 From: Cheng Da Date: Thu, 20 Oct 2022 21:02:30 +0800 Subject: [PATCH] Exchange "Produces" and "Consumes" (#1772) * Exchange "Produces" and "Consumes" * Added 'handleProducesAnnotation' and 'handleConsumesAnnotation' in the register of the method annotation 'RequestMapping'. And created a test case. * Reformatted the codes Co-authored-by: chengda Co-authored-by: Marvin Froeder --- .../java/feign/spring/SpringContract.java | 7 ++- .../java/feign/spring/SpringContractTest.java | 60 +++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/spring4/src/main/java/feign/spring/SpringContract.java b/spring4/src/main/java/feign/spring/SpringContract.java index d62cacedd..6413a3b0d 100755 --- a/spring4/src/main/java/feign/spring/SpringContract.java +++ b/spring4/src/main/java/feign/spring/SpringContract.java @@ -44,6 +44,9 @@ public SpringContract() { if (requestMapping.method().length == 1) data.template().method(Request.HttpMethod.valueOf(requestMapping.method()[0].name())); + + handleProducesAnnotation(data, requestMapping.produces()); + handleConsumesAnnotation(data, requestMapping.consumes()); }); @@ -83,7 +86,7 @@ public SpringContract() { }); registerMethodAnnotation(ResponseBody.class, (body, data) -> { - handleConsumesAnnotation(data, "application/json"); + handleProducesAnnotation(data, "application/json"); }); registerMethodAnnotation(ExceptionHandler.class, (ann, data) -> { data.ignoreMethod(); @@ -91,7 +94,7 @@ public SpringContract() { registerParameterAnnotation(PathVariable.class, pathVariableParameterAnnotationProcessor()); registerParameterAnnotation(RequestBody.class, (body, data, paramIndex) -> { - handleProducesAnnotation(data, "application/json"); + handleConsumesAnnotation(data, "application/json"); }); registerParameterAnnotation(RequestParam.class, requestParamParameterAnnotationProcessor()); registerParameterAnnotation(RequestPart.class, requestPartParameterAnnotationProcessor()); diff --git a/spring4/src/test/java/feign/spring/SpringContractTest.java b/spring4/src/test/java/feign/spring/SpringContractTest.java index 2acaee2eb..a01327682 100755 --- a/spring4/src/test/java/feign/spring/SpringContractTest.java +++ b/spring4/src/test/java/feign/spring/SpringContractTest.java @@ -16,17 +16,19 @@ import static org.hamcrest.Matchers.*; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import feign.Param; +import feign.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.*; -import feign.Feign; -import feign.Request; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; import feign.mock.HttpMethod; @@ -44,6 +46,11 @@ public class SpringContractTest { @Before public void setup() throws IOException { + Response.Builder response = Response.builder() + .status(200) + .body("hello world", StandardCharsets.UTF_8) + .headers(Collections.singletonMap("Content-Type", + Collections.singletonList("text/plain"))); mockClient = new MockClient() .noContent(HttpMethod.GET, "/health") .noContent(HttpMethod.GET, "/health/1") @@ -54,15 +61,46 @@ public void setup() throws IOException { .noContent(HttpMethod.GET, "/health/header") .noContent(HttpMethod.GET, "/health/header/map") .noContent(HttpMethod.GET, "/health/header/pojo") - .ok(HttpMethod.GET, "/health/generic", "{}"); + .ok(HttpMethod.GET, "/health/generic", "{}") + .add(HttpMethod.POST, "/health/text", response); resource = Feign.builder() .contract(new SpringContract()) .encoder(new JacksonEncoder()) - .decoder(new JacksonDecoder()) + .mapAndDecode(new TextResponseMapper(), new JacksonDecoder()) .client(mockClient) .target(new MockTarget<>(HealthResource.class)); } + class TextResponseMapper implements ResponseMapper { + @Override + public Response map(Response response, Type type) { + Map> headers = response.headers(); + if (headers == null || headers.isEmpty()) { + return response; + } + Collection head = headers.get("Content-Type"); + if (head == null || head.isEmpty()) { + return response; + } + String contentType = head.iterator().next(); + if (contentType.startsWith("text/plain")) { + try { + Reader reader = response.body().asReader(StandardCharsets.UTF_8); + char[] buff = new char[1024]; + String text = ""; + int n = 0; + while ((n = reader.read(buff)) > 0) { + text += new String(buff, 0, n); + } + response = response.toBuilder().body("\"" + text + "\"", StandardCharsets.UTF_8).build(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + return response; + } + } + @Test public void noPath() { resource.getStatus(); @@ -164,6 +202,14 @@ public void notAHttpMethod() { resource.missingResourceExceptionHandler(); } + @Test + public void testConsumeAndProduce() { + resource.produceText(new HashMap<>()); + Request request = mockClient.verifyOne(HttpMethod.POST, "/health/text"); + assertThat(request.headers(), hasEntry("Content-Type", Arrays.asList("application/json"))); + assertThat(request.headers(), hasEntry("Accept", Arrays.asList("text/plain"))); + } + interface GenericResource { @RequestMapping(value = "generic", method = RequestMethod.GET) @@ -178,6 +224,10 @@ interface HealthResource extends GenericResource { @RequestMapping(method = RequestMethod.GET) public @ResponseBody String getStatus(); + @RequestMapping(method = RequestMethod.POST, value = "/text", + produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public String produceText(@RequestBody Map data); + @RequestMapping(value = "/{id}", method = RequestMethod.GET) public void check( @PathVariable("id") String campaignId,