Block number of request using spring webflux - spring

I currently have below reactive service exposed using spring boot 2(Spring webflux)
#RequestMapping(value = "/tasks/v1", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono taskForUserV1(#RequestParam(value = "userId", required = true) String userId,
#RequestParam(required = false) Map<String, String> userData) {
return service.taskForUserV1(userId, userData);
}
But i want to throw an exception to the client requesting my service if the request are lets say more than 500. How to do it?

you can take a look at Rate Limiter design pattern implemented by resileince4j # https://resilience4j.github.io/resilience4j/#_rate_limiting
RateLimiter rateLimiter = RateLimiter.ofDefaults("testName");
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RateLimiterCallAdapter.of(rateLimiter))
.baseUrl("http://localhost:8080/")
.build();

Related

How to return application/pdf through Mono in a Reactive way

I am currently using Spring WebFlux to try build an async end-point, which fetches a PDF from a third-party end-point via Web Client before returning the PDF back to our API consumer. However, I am struggling with returning a Mono<ResponseEntity> with content type application/pdf due to the below exception:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class reactor.core.publisher.MonoMapFuseable] with preset Content-Type 'application/pdf']
Here is controller implementation. My question is:
Is my implementation in the right direction, or would I need to create some sort of converter?
Does Mono<ResponseEntity> even support returning a PDF as a response body?
#RequestMapping(value="/get-pdf", method = RequestMethod.GET)
public Mono<ResponseEntity> getPDFAsync() {
String url = "http://some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchangeToMono(response ->
Mono.just(ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF)
.body(response.bodyToMono(ByteArrayResource.class)
.map(byteArrayResource -> byteArrayResource.getByteArray())
)));
}
To download a file reactively, you could supply the file as a Flux<DataBuffer>, where DataBuffer is org.springframework.core.io.buffer.DataBuffer, like this:
// some shared buffer factory.
private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
#RequestMapping(value = "/download",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_PDF_VALUE}
)
public Mono<ResponseEntity<Flux<DataBuffer>>> downloadDocument(
...
) {
return Mono.fromCallable(() -> {
return ResponseEntity.ok(
DataBufferUtils.read(
new File("somepdf.pdf").toPath(),
dataBufferFactory,
8096
))
});
}
Or more specifically, since you seem to be using the WebFlux WebClient, you can forward the response body flux directly to your own response, without having to buffer the complete response first:
#RequestMapping(value = "/download",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_PDF_VALUE}
)
public Mono<ResponseEntity<Flux<DataBuffer>>> downloadDocument(
...
) {
String url = "http://some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchange()
.map(response -> response.bodyToFlux(DataBuffer.class))
.map(ResponseEntity::ok);
}
Hint: I hope you are reusing the WebClient instance and not instantiating a new one on each request.
I have found the answer! In short, returning Mono<byte[]>, and add produces = {MediaType.APPLICATION_PDF_VALUE} to #RequestMapping works. See example below.
#RequestMapping(value="/get-pdf", produces = {MediaType.APPLICATION_PDF_VALUE}, method = RequestMethod.GET)
public Mono<byte[]> getPdf() {
String url = "some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchangeToMono(response -> response
.bodyToMono(ByteArrayResource.class))
.map(byteArrayResource -> byteArrayResource.getByteArray());
}

Camel rest API to provide dynamic download

How can we provide document download using camel API, I need to provide an api using camel rest to response the file as download and I have the logic to create the pdf using apache fop, but i need to get some information how to respond the file as rest response using camel rest.
#RestController
public class MyController {
#Autowired
ICityService cityService;
#RequestMapping(
value = "/pdfreport",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE
)
public ResponseEntity<InputStreamResource> citiesReport() throws IOException {
List<City> cities = (List<City>) cityService.findAll();
ByteArrayInputStream bis = GeneratePdfReport.citiesReport(cities);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline;
filename = citiesreport.pdf");
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis));
}
}

Spring boot large file upload and download support

I have a spring boot web application which will handle large file (max size of 5g) upload and then save it to s3. The request and response may last for a long time.
So what is the best practice to handle the upload and download like this? How to make a good performance to prevent my server down when download or upload large files?
you can use multipart/form-data
#RequestMapping(value = "/agency/create", method = RequestMethod.POST, consumes = "multipart/form-data")
public ResponseEntity<List<String>> createAgency(
#RequestParam(value = "username", required = true) String username,
#RequestParam(value = "pic1", required = true)MultipartFile pic1File,
MultipartHttpServletRequest request, ModelAndView modelAndView) {
List<String> requestKeys=new ArrayList<String>();
List<String> originalFileName=new ArrayList<String>();
request.getFileNames().forEachRemaining(requestKeys::add);
for(String multipartFile:requestKeys) {
originalFileName.add(request.getFile(multipartFile).getOriginalFilename());
}
storageService.store(pic1File);
return new ResponseEntity<List<String>>(originalFileName, HttpStatus.CREATED);
}
Posting in case someone finds this useful in the future. This works with a REST controller as of Spring Boot 2.4.2.
Class annotations:
#RestController
#RequestMapping("/api")
Method declaration:
#RequestMapping(path = "/file-upload/{depot}/{fileName}", method = {RequestMethod.POST, RequestMethod.PUT})
public ResponseEntity<String> fileUpload(
#PathVariable(name = "depot") String depot,
#PathVariable(name = "fileName") String fileName,
InputStream inputStream,
HttpServletRequest request,
HttpServletResponse response)
The above is the Spring Boot configuration for a REST Controller that worked for me for large file upload. The key was adding InputStream inputStream directly.

Using Feign #HeaderMap with Springboot 1.5.4.RELEASE does not send headers

I am trying to use feign.HeaderMap annotation to pass a map of HTTP headers in the rest request but these are appearing in the body.
Code below:
#FeignClient(name = "accounts", url = "localhost:8080")
public interface AccountClient {
#RequestMapping(method = RequestMethod.GET, value = "/rest/accounts/get", produces = MediaType.APPLICATION_JSON_VALUE)
Account findOne(#RequestParam("id") String id, #HeaderMap Map headers);
}
You are mixing annotations. When using spring-cloud-netflix you will need to use the Spring annotation #RequestHeader instead.
#RequestMapping(method = RequestMethod.GET,
value = "/rest/accounts/get",
produces = MediaType.APPLICATION_JSON_VALUE)
Account findOne(#RequestParam("id") String id, #RequestHeader Map headers);
In Feign by default, all parameters not annotated will be serialized in the Body.

#RequestBody Annotation not working on Websphere Application Server

I am trying to use #RequestBody annotation in one of my controllers method:
#Auditable(application = AuditApplication.DEP_TRXN, actionCategory = AuditActionCategory.READ_RESPONSE, actionDetail = "Viewed a tracking group.", event = AuditEventType.ACTION)
#RequestMapping(value = "groupView", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody
String ajaxGroupView(#RequestBody String payload, final HttpServletRequest request) throws TrackingServiceException, JsonGenerationException, JsonMappingException,
IOException
{
String requestBody = java.net.URLDecoder.decode(payload, "UTF-8");
return requestBody;
}
When i run this code on Jetty server my 'payload' gets the post data, but when i run the same code on Websphere Application Server -8, payload is 'null'. I have mvc annotation driven turn on. How do i make this code work on Websphere Application Server?
Thanks!

Resources