0

We are currently rewriting some legacy service to spring framework with web-flux. Because legacy logic allows receiving payload or multipart data on GET request, we need to recreate that behavior in our new service.

Web-flux doesn't allow us to receive payload or multipart data in case of Get request. I tested this behavior in @RestController and @Controller. Is it possible to change configuration for web-flux to be able to handle such cases?

Example of UploadFileController:

@Controller
public class UploadController {

    @GetMapping(value = "/upload")
    public @ResponseBody Mono<ResponseEntity<String>> upload(@RequestBody Flux<Part> parts) {
        System.out.println("Upload controller was invoked");
        return parts.next()
            .flatMap(part -> DataBufferUtils.join(part.content()))
            .map(this::mapDataBufferToByteArray)
            .map(data -> {
                String uploadedData = new String(data);
                System.out.println("Uploaded file data: " + uploadedData);
                return ResponseEntity.ok(uploadedData);
            });
    }

    private byte[] mapDataBufferToByteArray(DataBuffer buffer) {
        byte[] data = new byte[buffer.readableByteCount()];
        buffer.read(data);
        return data;
    }
}

public class UploadControllerTest {

    @Autowired
    private TestRestTemplate testRestTemplate;

    @Test
    public void shouldUpload() {
        // given
        final HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

        LinkedMultiValueMap<String, Object> parameters = new 
        LinkedMultiValueMap<>();
        parameters.add("file", "Test");

        // when
        ResponseEntity<String> response = 
        testRestTemplate.exchange("/upload",
                HttpMethod.GET,
                new HttpEntity<>(parameters, httpHeaders),
                String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }
}
  • please post a working example showing it doesnt work – Toerktumlare Mar 14 '20 at 20:27
  • Hi. I have posted code showing that test doesn't work. If you change GET to POST every thing will work like a charm. – Marcin Sobejko Mar 15 '20 at 05:41
  • Im going to answer ”probably no” on your request and why anyone would like to send a body in a GET request is to me insane https://stackoverflow.com/questions/978061/http-get-with-request-body good luck (you will probably need it, if you dont decide to actually fix this shitty legacy) – Toerktumlare Mar 15 '20 at 10:06
  • @ThomasAndolf thnaks, you very much. I just want to be sure that I understand this spring GET behavior correctly. I will try to work around that problem. Thanks. – Marcin Sobejko Mar 15 '20 at 12:58

1 Answers1

2

What about creating webfilter which will transform incoming get requests into post internally

@RestController
public static class GetPostHandler {
    @PostMapping("/test")
    public Flux<String> getName(@RequestPart("test") String test, @RequestPart("test2") String test2) {
      return Flux.just(test,test2);
    }
}


@Component
public class GetPostFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange,
                             WebFilterChain webFilterChain) {
        ServerHttpRequest req = serverWebExchange.getRequest().mutate().method(HttpMethod.POST).build();

        return webFilterChain.filter( serverWebExchange.mutate().request(req).build());
    }
}

I have tested

curl -X GET \
  http://localhost:8080/test \
  -H 'cache-control: no-cache' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F test=1 \
  -F test2=2

and result is correct:

12

Nonika
  • 2,339
  • 11
  • 14
  • 1
    Thanks, you very much! This works! I have tested it before but it didn't work because I didn't know that TestRestTemplate will not add payload to the request on GET. I was forced to add custom test configuration to allow TestRestTeamplate GET with payload. To configure TestRestTemplate there need to be imported custom test configuration with TestRestTemplateBuilder supplayed with custom SimpleClientHttpRequestFactory#prepareConnection. – Marcin Sobejko Mar 18 '20 at 19:52
  • Thats why I add curl snippet :) It depends on http clients. But as you had legacy one I thought you will use curl to test – Nonika Mar 18 '20 at 19:58