Apache CXF - http-centric approach and PUT from client to server - java

I use Apache CXF to provide communication with a RESTful API.
I need to use a PUT method to send some constrained entities to database via the API.
Is this the right way to provide this method?
I ask because I am getting a HTTP 500 error code response.
I can only find GET method examples in the official Apache CXF documentation; HTTP PUT, HTTP POST etc. are missing.
WebClient client =
WebClient.create("http://MY_SERVER:9090/admission/services/processing");
Admission a = new Admission();
a.setCode("73935282");
:
:
Response r = client.path("/admission").put(a);
// Here I would like to get 201, but there is 500 :(
System.out.println("response: " + r.getStatus());

Could it be that the service is expecting content type other than XML, like JSON? The default behavior for WebClient is to assume content-type is application/xml.
Here's the relevant source code for WebClient.java:
protected Response doInvoke(String httpMethod, Object body, Class<?> responseClass, Type genericType) {
MultivaluedMap<String, String> headers = getHeaders();
if (body != null) {
if (headers.getFirst(HttpHeaders.CONTENT_TYPE) == null) {
headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_TYPE.toString());
}
} else {
headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD);
}
if (responseClass != null && headers.getFirst(HttpHeaders.ACCEPT) == null) {
headers.putSingle(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_TYPE.toString());
}
resetResponse();
return doChainedInvocation(httpMethod, headers, body, responseClass, genericType, null, null);
}
If so, you can set the content type on WebClient using the type() method. For example, to have the client produce JSON:
WebClient client = WebClient.create("http://MY_SERVER:9090/admission/services/processing");
client.type(MediaType.APPLICATION_JSON_TYPE);
Admission a = new Admission();
a.setCode("73935282");
Response r = client.path("/admission").put(a);

Related

Get request header in spring boot

How do I get the header and body of the current request from an application which called my Springboot application? I need to extract this information. Unfortunately this does not work. I tried to get the current request with this code sample (https://stackoverflow.com/a/26323545/5762515):
public static HttpServletRequest getCurrentHttpRequest(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
return request;
}
throw new IllegalArgumentException("Request must not be null!");
}
And then I tried to get the body
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) currentRequest;
String requestBody = new String(requestWrapper.getContentAsByteArray());
Can someone tell me what im doing wrong?
Thanks in advance
#RestController
public class SampleController {
#PostMapping("/RestEndpoint")
public ResponseEntity<?> sampleEndpoint(#RequestHeader Map<String, String> headers,#RequestBody Map<String,String> body) {
//Do something with header / body
return null;
}
}
If the application's are communicating through a rest endpoint I believe this would be the simplest solution. In spring you can add RequestHeader and RequestBody annotations to method arguments to have them setup to be used.
Of course you can map RequestBody directly to some POJO instead of using a map but just as an example.
Let me know if this is what you were looking for !
#TryHard, You're using spring boot then following way is more preferable for you,
#RestController
public class SampleController {
#RequestMapping("/get-header-data")
public ResponseEntity<?> sampleEndpoint(HttpServletRequest request) {
// request object comes with various in-built methods use as per your requirement.
request.getHeader("<key>");
}
}
you can get header with your code but need apply some changes.
private String getRequest() throws Exception {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs != null) {
HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();
return request ;
}
throw new IllegalArgumentException("Request must not be null!");
}
after you can extract header info from request. For example if you want get Accept-Encoding
String headerEncoding = getRequest().getHeader("Accept-Encoding");
obliviusly you don't use this approce if not necessary.
If you want exract the body NOT use this solution

How to throw exception based on feign.Response?

I have a Feign client with a method returning the feign.Response class. When another service throws an exception, feign puts an exception message on response body and puts status, but my service does not throw an exception. Can I throw an exception based on what I received in response like when I use ResponseEntity.
Feign client
#FeignClient(name = "ms-filestorage")
#RequestMapping(value = "/files", produces = "application/json")
public interface FileStorageApi {
#GetMapping(value = "/{id}")
Response getFileById(#PathVariable String id);
}
Usage of client
#Override
public Response getFileFromStorage(String fileId) {
Response fileStorageResponse = fileStorageApi.getFileById(fileId);
// NOW I USE THIS WAY FOR CHECKING RESPONSE BUT IT DOESN'T LOOK GOOD
//if (fileStorageResponse.status() != HttpStatus.OK.value()) {
// throw new OsagoServiceException();
//}
return fileStorageResponse;
}
Usually, if a Feign client call receives an error response from the API it is calling, it throws a FeignException.
This can be caught in a try / catch block (or a Feign ErrorDecoder if you want to be more sophisticated, but that's another post).
However, this is not the case if you map the error response into a Feign.Response return type - see this Github issue.
Instead of returning Feign.Response from getFileFromStorage(), you should create a custom Java object to hold the response, and you will then have access to the FeignException which you can handle as you wish.
Note that if you don't need access to the data that is returned from the API you are calling, changing the return type to void will also resolve this issue.

Spring http outbound gateway - how to return a ResponseEntity with a body

I'm probably barking up the wrong tree with this, but I'm having some difficulty with Spring Integration and a http outbound-gateway.
I can configure it so that it makes a http POST and I get the response body as a simple String like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
expected-response-type="java.lang.String"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
String getHotelsList(final Map<String, String> parameters);
}
And I can configure it so that I get a ResponseEntity back like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
ResponseEntity<String> getHotelsList(final Map<String, String> parameters);
}
Both versions of the code work. However, when returning a String I get the response body, but I don't get the http status and headers etc.
But when I use the ResponseEntity version I get the http status and headers, but I always get a null body via ResponseEntity#getBody
Is there anyway I can get both the body and the http status and headers?
(Ignoring the fact that the expedia hotels api returns JSON - at the moment I just want to get the raw body text)
Some further info which helps clarify the problem I am seeing. If I put a wire-tap on the response channel:
When I've configured it to return a simple String I get:
INFO: GenericMessage [payload={"HotelListResponse":{"EanWsError":{"itineraryId":-1,"handling":"RECOVERABLE","category":"AUTHENTICATION","exceptionConditionId":-1,"presentationMessage":"TravelNow.com cannot service this request.","verboseMessage":"Authentication failure. (cid=0; ipAddress=194.73.101.79)"},"customerSessionId":"2c9d7b43-3447-4b5e-ad87-54ce7a810041"}}, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded, timestamp=1433403827133}]
You can see the full response body in the payload, and notice the Content-Length being set to 337
Conversely, when I use a ResponseEntity<String> I get:
INFO: GenericMessage [payload=<200 OK,{Transaction-Id=[5f3894df-0a8e-11e5-a43a-ee6fbd565000], Content-Type=[application/json], Server=[EAN], Date=[Thu, 04 Jun 2015 07:50:30 GMT], Content-Length=[337], Connection=[keep-alive]}>, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=9a598432-99c9-6a15-3451-bf9b1515491b, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433404230000, Content-Type=application/x-www-form-urlencoded, timestamp=1433404230465}]
The Content-Length is still set to 337, but there is no response body in the payload
Notice that you don't use any expected-response-type for the second case.
The RestTemplate works this way in case of no expected-response-type:
public ResponseEntityResponseExtractor(Type responseType) {
if (responseType != null && !Void.class.equals(responseType)) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
#Override
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return new ResponseEntity<T>(body, response.getHeaders(), response.getStatusCode());
}
else {
return new ResponseEntity<T>(response.getHeaders(), response.getStatusCode());
}
}
As you see it really returns the ResponseEntity without body.
And Spring Integration can do nothing here on the matter...
From other side let's take a look if you really need a whole ResponseEntity as a reply back from the <int-http:outbound-gateway>.
Maybe headerMapper would be enough for you?.. For example http status is here already, even in your logs from the question:
Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded,

Why RestTemplate GET response is in JSON when should be in XML?

I struggled with an extrange spring behavior using RestTemplate (org.springframework.web.client.RestTemplate) without success.
I use in my hole application below code and always receive an XML response, which I parse and evaluate its result.
String apiResponse = getRestTemplate().postForObject(url, body, String.class);
But can't figure out why a server response is in JSON format after executing:
String apiResponse = getRestTemplate().getForObject(url, String.class);
I've debugged at low level RestTemplate and the content type is XML, but have no idea why the result is in JSON.
When I access from a browser the response is also in XML, but in apiResponse I got JSON.
I tried many options after reading Spring documentation
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/web/client/RestTemplate.html
Also tried to modify explicitly the headers but still can't figure it out.
I debugged RestTemplate class and noticed that this method is always setting application/json:
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (responseType != null) {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canRead(responseType, null)) {
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
allSupportedMediaTypes.add(supportedMediaType);
}
}
}
if (!allSupportedMediaTypes.isEmpty()) {
MediaType.sortBySpecificity(allSupportedMediaTypes);
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
}
Could you give an idea?
I could solve my issue with RC.'s help. I'll post the answer to help other people.
The problem was that Accept header is automatically set to APPLICATION/JSON so I had to change the way to invoke the service in order to provide the Accept header I want.
I changed this:
String response = getRestTemplate().getForObject(url, String.class);
To this in order to make the application work:
// Set XML content type explicitly to force response in XML (If not spring gets response in JSON)
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = getRestTemplate().exchange(url, HttpMethod.GET, entity, String.class);
String responseBody = response.getBody();

How to send JSON data in request body suing apache CXF webclient?

I am using apache cxf webclient to consume a service written in .NET
sample JSON to be sent in request body to a web service
{
"Conditions":
[
{
"Field":"TextBody",
"Comparer":"ContainsAny",
"Values":["stocks","retire"],
"Proximity":0
},
{
"Field":"SentAt",
"Comparer":"LessThan",
"Values":["1331769600"],
"Proximity":0
},
],
"Operator":"And",
"ExpireResultIn":3600
}
Is there any way if I want to submit data from both form and in Json body in one request ?
webclient API apache CXF -
web client API doc
WebClient client = WebClient.create("http://mylocalhost.com:8989/CXFTest/cxfws/rest/restservice/json");
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
After this which method and how to use ?
client.form(...form object )
client.post(...JSON string )
They have not shared Object of "Conditions" in JSON which I can annotate and pass to post method of client
I got answer here
Need to set JSON provider in my case it was jackson
List<Object> providers = new ArrayList<Object>();
providers.add( new JacksonJaxbJsonProvider() );
WebClient client = WebClient.create("http://localhost:8080/poc_restapi_cxf/api",
providers);
client = client.accept("application/json")
.type("application/json")
.path("/order")
.query("id", "1");
Order order = client.get(Order.class);
System.out.println("Order:" + order.getCustomerName());
There is a way to do this using annotations and suited my purpose:
#Post
#Path("mypath/json/whatever")
#Consumes({MediaType.APPLICATION_JSON_VALUE})
public Response postClient(#Context HttpHeaders headers, String input) {
//Here the String input will be equal to the supplied json.
//...
}

Categories

Resources