Outbound Gateway post request to spring MVC controller - java

I have try to sent payload that have type MultiValueMap to spring MVC controller via Outbound gateway. But it seem to be that map data not coming to the controller. Don't know what is wrong or missing. and my code is be like this:
Outbound config:
<int-http:outbound-gateway id="outGateway"
http-method="POST"
request-channel="responseChannel"
url="http://localhost:8081/SpringIntegration-In-out-test/responseReq"
extract-request-payload="true">
</int-http:outbound-gateway>
Controller Config:
#RequestMapping("/responseReq")
public ModelAndView goResponse(#RequestBody MultiValueMap<String,String> body){
Iterator it = body.entrySet().iterator();
while (it.hasNext()) {
MultiValueMap.Entry pairs = (MultiValueMap.Entry)it.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue());
it.remove();
}
return new ModelAndView("response");
}
I use Iterator to get map value but it have nothing.

Eveything looks good.
I recommend you to 'sniff' your HTTP traffic and take a look what's going on with your body. On Windows I use TCP Trace. Here you should be sure that you really send a Map as payload.
From other side test your Controller separately, e.g. using some RestClient. My preference - Firefox plugin. Build here the form manually, which should be converted to the MultiValueMap in the DispatcherServlet and don't forget to present header Content-Type: application/x-www-form-urlencoded

If your outbound adapter is to be used in a unidirectional way (send-only), you should use outbound-channel-adapter :
<int-http:outbound-channel-adapter
id="outAdapter"
channel="responseChannel"
url="http://localhost:8081/SpringIntegration-In-out-test/responseReq"
http-method="POST"
charset="UTF-8"
/>
Secondly, make your request mapping more precise by defining RequestMethod as POST:
#RequestMapping(value="/responseReq", method=RequestMethod.POST)
Lastly, why you are converting your Spring Integration Message into JMS Message by setting extract-request-payload="true".

Related

Getting a 400 Bad Request when Angular HttpClient posts a Set<> to a SpringBoot endpoint

I am exposing an endpoint that accepts a Set<> as a #RequestBody this way :
public #ResponseBody ResponseEntity<Response> addTeamOwner(#RequestParam("teamName") String teamName, #RequestBody Set<String> emails, HttpServletRequest request){...}
And my Angular frontend is calling this endpoint like this :
let params = new HttpParams().set('teamName', teamName);
let url = `${UrlManager.TEAMS}/addOwners?${params.toString()}`;
this.httpClient.post<any>(url, emails);
For some reason I'm getting 400 Bad Request : HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: 'Bad Request', url: 'http://localhost:4200/api/teams/addOwners?teamName=DEMO_TEAM', ok: false, …}
It seems that the Set that Angular is sending is not accepted by the backend because when I change to an Array everything works fine !
FYI, my API is SpringBoot and my frontend is Angular.
Actually it is not possible to serialize data sent within Set because the data are not stored as properties.
The solution was to convert the set to an array this way :
this.httpClient.post<any>(url, [...emails]);
and the backend is able to deserialize it as a Set correctly.

feign.FeignException: status 401 error when REST API using a feign client tries to connect

I have a Spring Boot REST API written in Java. I am using a Feign client to connect to another REST API. The endpoint uses to header parameters, apikey and serviceName.
I get this error when the endpoint calls the feign client.
feign.FeignException: status 401 reading FacilityViewClient#getFacilities(Map,String,String)\r\n\tat feign.FeignException.errorStatus(FeignException.java:78)
This is how I have implemented the feign client with header parameters:
#GetMapping(path = "/schedule-svc/api/v1/facilities")
FacilitiesViewResponse getFacilities(#RequestHeader Map headers,
#RequestParam("facilityType") String facilityType,
#RequestParam("stateProvinceCode") String stateProvinceCode);
This is the call using the feign client:
Map<String, Object> headerMap = new HashMap<>();
headerMap.put("apikey", "xxxxxxx" );
headerMap.put("SERVICE-NAME", "Location");
FacilitiesViewResponse facilitiesViewResponse = facilityViewClient.getFacilities( headerMap,"RALYD", "PA");
I have also tried to use individual #RequestHeader string parameters and get the same error. Like this:
#RequestHeader("apikey") String apiKey
#RequestHeader("SERVICE-NAME") String serviceName
The same error occurs.
I can hit the service using Postman and the header information looks like this:
Are there other properties that need to be defined in the feign client to successfully set the header parameters
we solved this problem. The url and path were wrong on the FeignClient. The error was misleading us to think it was an unauthorized error when actually it was a 404 error.
Add below code in your feign client configuration (in application.yml file)
feign:
hystrix:
enabled: true

Spring MVC Override Received Content Type

I'm working on a Spring MVC application and have a client that I have no control over. This client is POSTing JSON data but transmitting a application/x-www-form-urlencoded header. Spring naturally trusts this header and tries to receive the data but can't because its JSON. Has anyone had experience overriding the header that Spring receives or just specifying exactly what type of data is coming, regardless of the headers?
You can do two things;
Change the client to send the Content-Type:
application/json header
Write a Servlet Filter or Spring Interceptor which is on top of the Spring Controller and checks for the header Content-Type. If it is not application/json then it changes it to application/json.
Why don't you write a separate controller to handle application/x-www-form-urlencoded requests. If the request is a valid JSON, then you can parse it and forward it to to appropriate service.
This way you can also handle a case in future where you get request of same type which is not a valid JSON.
#RequestMapping(value = "/handleURLEncoded", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public #ResponseBody Object handleURLEncoded(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
//now you have your request as a String
//you can manipulate it in any way
if(isJSONValid(json)) {
JSONObject jsonObj = new JSONObject(json);
//forward request or call service directly from here
//...
}
//other cases where it's not a valid JSON
}
Note: isJSONValid() method copied from this answer

Spring Boot + Zuul: how to get the "Content-length" header from a file stream provided by RESTful services?

I am using Spring Cloud and Zuul proxy as gateway to my RESTful service provided by a microservice. When I perform a request directly to an instance of a microservice, all the headers are provided as I expected. However, when the same request is proxied by Zuul, the header "Content-length" is removed. I've made some research about it and I saw that Zuul adds the header "Transfer-Encoding" as "chunked" and in this case the header Content-length is omitted (Content-Length is being stripped, Spring Cloud Netflix: Whats happening in ZuulConfiguration with the ZuulServlet?).
However, I really need to get the "Content-length" provided by my RESTful service. This request also must be proxied by Zuul (I have many instances of a microservice, so I wouldn't access them directly).
Here is the method in my microservice:
#RequestMapping(value = "/jobresult/{id}", method = RequestMethod.GET)
#Timed
public ResponseEntity<InputStreamResource> downloadJobResult(#PathVariable Long id) {
Job job = jobService.findOne(id);
File file = new File(job.getTargetFile());
try {
return ResponseEntity.ok().contentLength(file.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).body(new InputStreamResource(new FileInputStream(file)));
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
}
}
For example, the request to /api/jobresult/1 provides the header "Content-length" correctly, but the request to /service/api/jobresult/1 (routed by Zuul) do not show this header and also modify the "Transfer-Encoding" to "chunked".
The response filter for Zuul from Spring Cloud Netflix code is causing the issue.
Solution
Add an application.properties file in your src/main/resources if you don't have it and add the following line:
zuul.set-content-length=true
Unfortunately I couldn't find a answer for this problem. To make the things work, I wrote an alternative header "X-Content-Length" containing the file size.
In this way, Zuul does not erase the header and I can read it in the client side. As I have the control of both, it is not a problem. But in other cases, the clients should be aware of such header.

Custom Stomp Headers using Spring websockets

I have a basic spring websocket application which currently sends basic data to subscribers.
Currently the system uses the SimpMessageSendingOperations class as the message handler.
If I call SimpMessageSendingOperations.convertAndSend(destination, object) then the object is converted and received by the subscribed clients.
I would like to be able to send a custom header to the clients.
I have tried using the SimpMessageSendingOperations.convertAndSend(destination, object, headers) method to do this. However the custom header is not included in the stomp message.
Debugging through the code it looks like StompHeaderAccessor.toStompHeaderMap() method calls
toNativeHeaderMap() which uses the native header and the original native header maps to build up the stomp headers.
Is there a way to get a custom header added to stomp message?
StompHeaderAccessor extends NativeMessageHeaderAccessor which seems to be where the non-stomp headers live, except they are all stored in a single header called nativeHeaders - which itself is a map.
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public GenericMessage<Greeting> greeting(HelloMessage message) throws Exception {
Map<String, List<String>> nativeHeaders = new HashMap<>();
nativeHeaders.put("hello", Collections.singletonList("world"));
Map<String,Object> headers = new HashMap<>();
headers.put(NativeMessageHeaderAccessor.NATIVE_HEADERS, nativeHeaders);
return new GenericMessage<Greeting>(new Greeting("Hello, " + message.getName() + "!"), headers);
}
A simple interceptor server-side to wrap your custom headers to the nativeHeaders header should be enough to expose them client-side where they would be available as a map message.headers.nativeHeaders. Simmilarly, you could write a client-side interceptor to move the nativeHeaders into the regular headers - so before your client is aware of the message, all the expected headers are simply in the message.headers.

Categories

Resources