In WebFlux-WebFilter, how can I identify whether an inbound HTTP request has a body or not? - spring

In WebFilter, how can I identify whether an inbound HTTP request has a body or not? I am aware of the Content-Length header presence check, but it's a hack at the best. Besides, it will not catch all the cases (some of the clients are sending the body without the Content-Length header).
Note: I just need to identify whether the body is there or not, reading body is a whole different question!
The server is Netty, if that helps.

In WebFlux you can use the 'HttpMessageReader' interface to determine whether an inbound HTTP request has a body or not. This interface is used to read the body of a request, so you can check if there is a registered 'HttpMessageReader' that supports the 'Content-Type' of the request.
If there is such a reader, then the request has a body; if not, then the request doesn't have a body.
Sample code:
HttpMessageReader httpMessageReader = new HttpMessageReader();
if (httpMessageReader.canRead(MyRequestType.class, MediaType.APPLICATION_JSON)) {
// if canRead returns true, there is a body
} else {
// if canRead returns false, there is no body
}

Related

spring cloud gateway, avoid routing to a uri

I'm looking for a way to execute some filters and predicates on a request, and at the end simply return a response to the user, instead of routing it to a specific URI.
For example, a user is calling /auth/token and my gateway has a filter that generates a token and transforms the body of the response (using the ModifyResponseBodyGatewayFilterFactory).
When adding a filter that simply returns response.setCompleted(), the body returns empty and the status code is always 200.
return (exchange, chain) -> {
return modifyResponseBodyGatewayFilterFactory.apply(c -> c.setRewriteFunction(Object.class, String.class, SomeBody))
.filter(exchange, chain)
.then(exchange.getResponse().setComplete());
}
How can I return a specific body to the user, without routing to a URI?
Thanks in advance!
I couldn't find a solution, so instead, I've created a web flux controller for this specific request.
This is a good enough solution for me.

Feign get request with body

For some reason I need to call a GET method API and pass json request body for it. I really couldn't find an example for it. I wonder if it is even supported using feign.
How can I do that using feign?
Yes, Feign supports it. You can do the same as with POST requests:
#FeignClient(name = "clientName", url = "http://localhost:8888")
public interface SampleFeignClient {
#GetMapping("/remote")
String test(#RequestBody SampleRequestBody sampleRequestBody);
}
But be aware: a lot of servers ignore body or even refuse that kind of "non-standard" requests completely (GET or HEAD with request bodies).
According to the documentation the correct way to do it would be to use the #SpringQueryMap annotation.
#FeignClient(name = "clientName", url = "http://localhost:8888")
public interface SampleFeignClient {
#GetMapping("/remote")
String test(#SpringQueryMap SampleRequestBody sampleRequestBody);
}
You can find more information here

Spring receiving empty body

I'm using Spring for my backend. I have the following code:
#RequestMapping(value = "/test", method = RequestMethod.POST)
#CrossOrigin
public void test(HttpServletRequest request) {
System.out.println(request.getParameterMap().size());
System.out.println(new JSONObject(request.getParameterMap()));
}
When I send JSON data using Postman, I get a map of all the parameters I've sent.
But when I'm making the same call from my website, I get an empty map with size 0. I do not get any error or exception on both front and back sides.
What could be the reason?
Thank you
Most probably getParameterMap() is really empty in your case, because parameters are not passed as query, but as a body (content) of HTTP request when it is sent from your web-site.
It can also be affected by the Content-Type and Accept headers of the HTTP request.
According to official documentation ServletRequest.getParameterMap() returns:
For HTTP servlets, parameters are contained in the query string or posted form data.
Usually "posted form data" implies: HTTP header Content-Type: application/x-www-form-urlencoded and URL encoded name-value pairs of parameters in the content of HTTP request.
If your web-site sends application/json, any other content type, or does not define content type at all, it might not be properly mapped into the request parameters by servlet container. In this case you should look into the body of HTTP request (ServletRequest.getReader()) to get the payload, or let Spring MVC do that (e.g. #RequestBody annotation).

Multiple Content-type in Spring MVC

Can we have multiple content-type in Spring MVC request header?
I'm passing:
{Content-type = application/json, text/plain}
through Postman to my API. Currently, I'm getting org.springframework.web.HttpMediaTypeNotSupportedException: Invalid mime type ....
I wanted to know, is there something with my input values, or we can't have multiple content-type in our header.
Controller:
#RequestMapping(value = "/addressees", produces = APPLICATION_JSON_UTF8_VALUE, method = GET)
Yes, spring mvc request mapping supports multiple consumes MIME type , sample looks like
#RequestMapping(value = "/something", method = PUT,
consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE},
produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public SomeObject updateSomeObject(SomeObject acct) {
return doStuff(acct);
}
Add consumes part in requestmapping like - consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE}
For know more, refer this link -
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html
Your request header can have one content-type per request. You specify to the server what type of data are actually being sent.
Your server/API endpoint can support multiple.
So if your request specifies both application/json and text/plain at the same time, I believe it is a problem with your request.
Yes, RequestMapping.consumes accepts an array of Mime types
String[] consumes() default {};
Note that you have to use consumes to define the incoming MIME types. produces is for the outgoing type.

Redirecting a signal returning an error to another signal

So my use case is that I have an HTTP request (fired using -[NSURLSession rac_dataWithRequest:]) that can return a status code of 403.
When this happens I would like to catch that, and redirect to another SignalProducer that requests an authentication token. When that SignalProducer successfully completes, I would like to redirect back to the original rac_dataWithRequest.
How would I best do this? I'm using ReactiveCocoa 4.x and Swift 2.
do you need to hand the authentication token back to the original request before retrying? If yes, then this won't help you, sorry.
I assumed that you just need to fire the authentication request once and then retry the original request. E.g. your authentication request sets the HTTP Authorization header of your HTTP client so that the next request is automatically authorised...
This gist has the contents of a Playground that illustrates the solution. This gist is the result of me playing around to find a solution, requests are mocked, ...
Lets say you have a SignalProducer for your request:
let request = SignalProducer<Response, NSError> {
// Perform your request that needs authentication here
}
And another one for your authentication request:
let authorize = SignalProducer<Void, NSError> {
// Perform your request that needs authentication here
}
Now you can use flatMapErrors (formerly catch) and retry:
request.flatMapError { error -> SignalProducer<String, NSError> in
if(error.code == 403) {
return authorize.then(SignalProducer(error: error))
} else {
return SignalProducer(error: error)
}
}.retry(1)
So, we catch errors of the original request and, if the error code is 403, we perform the authorize request. Then, we still forward the catched error. Otherwise, we just forward the error.
Forwarding the error after the authorize request is important, otherwise, retry will not restart the original request.
The problem with this however is, that it will retry on every error, not just when the original request returns 403...

Resources