i'm trying to make a RESTful WS to upload a file with CXFRS camel component, i'm trying to retrive the uploaded file via getAttachment method, but it is always empty.
This is my code:
EndPoint class: ExposedApi.java
#Path("/test")
public class ExposedApi {
#POST
#Path("/resources/solver")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response upload(#Multipart(value = "file") Attachment attachments ){
return null;
}
}
Code that extends routebuolder: RouteConf.java
public class RouteConf extends RouteBuilder {
#Override
public void configure() throws Exception {
// TODO Auto-generated method stub
getContext().setTracing(true);
from("cxfrs://http://localhost:9090/test?resourceClasses=org.foo.ExposedApi")
.streamCaching()
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
Message inMessage = exchange.getIn();
String operationName = inMessage.getHeader(CxfConstants.OPERATION_NAME, String.class);
if (operationName=="upload"){
Map<String, DataHandler> names= inMessage.getAttachments();
exchange.getOut().setBody(inMessage.getAttachmentNames().toString()+ " TEST "+ names.keySet().toString());
}
}
});
}
}
Curl request:
curl -v -F "file=#/Users/Massimo/Desktop/ic_eb.png" http://localhost:9090/test/test/resources/solver
and this is the response
* Adding handle: conn: 0x7f92cb804000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7f92cb804000) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 9090 (#0)
* Trying ::1...
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9090 (#0)
> POST /test/test/resources/solver HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:9090
> Accept: */*
> Content-Length: 89280
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------837830fae872
>
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Fri, 10 Oct 2014 14:54:51 GMT
< Content-Length: 10
* Server Jetty(7.6.9.v20130131) is not blacklisted
< Server: Jetty(7.6.9.v20130131)
<
* Connection #0 to host localhost left intact
[] TEST []
What am i doing wrong? how i can retrive the attached file?
after a bit of work i've find the solution.
the attachment is inside the body.
If you want retrive the attachment this line of code worked for me
Attachment att = (Attachment)inMessage.getBody(ArrayList.class).get(0);
Related
I've created a route to allow me to forward a REST call. Everything is going well, except I cannot modify the HTTP headers of the response (actually I can't even get them untouched on the response).
// My processor
private void remplacerLiensDansHeader(final Exchange exchange, final String rootUrlPivotJoram, final String rootUrlRemplacement) {
// That is OK, I get the values just fine
ArrayList<String> oldLinks = exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
// This is also OK
final List<String> newLinks = anciensLiens.stream().map(lien -> lien.replace(rootUrlPivotJoram, rootUrlRemplacement)).collect(toList());
// No error, but apparently doesn't work
exchange.getMessage().setHeader(HEADER_LINK, newLinks);
exchange.getMessage().setHeader("test", "test");
}
// Route configuration
#Override
public void configure() throws Exception {
this.from(RestRouteBuilder.endPoint(createProducerJoramConfiguration))
.setExchangePattern(InOut)
.removeHeader(Exchange.HTTP_URI)
.toD(createProducerJoramConfiguration.getUrlDestination())
.setHeader("test", () -> "test") // that doesn't work either
.process(createProducerJoramConfiguration.getProcessor()); // this is the processor with the code above
}
This is the response I get (note that the response code is 200 and I think it's weird as the original is 201)
curl -v -XPost --user "xxx:yyyy" http://localhost:10015/zzzz/webservices/xxxxx
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10015 (#0)
* Server auth using Basic with user 'xxx'
> Post /zzzzz/webservices/eeee HTTP/1.1
> Host: localhost:10015
> Authorization: Basic pppppppppppppppppp==
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200
< Date: Tue, 31 Aug 2021 11:17:49 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Two things I've noticed:
if I add a body in the processor, then the body is present in the response,
if I remove the processor, the headers present in the "original response" are not present either.
I don't know what headers you exactly lose, but be aware that the Camel HTTP component has a default header filter (as lots of Camel components have).
If you don't specify your own HeaderFilterStrategy, the default HttpHeaderFilterStrategy is used.
This default filters the following headers:
content-length
content-type
host
cache-control
connection
date
pragma
trailer
transfer-encoding
upgrade
via
warning
Camel*
org.apache.camel*
With this filter, Camel wants to avoid that old HTTP headers are still present on outgoing requests (probably with wrong data).
The filtering of Camel headers is just to remove Camel specific stuff that is not relevant for HTTP.
Actually, the problem was the cxfrs component.
We manage to find an answer here : see : Response to REST client from Camel CXFRS route?
Here is the final solution.
Thanks to everyone that looked or answer, I hope that'll help someone else.
public class ModificationHeaderLinkProcessor implements Processor {
private static final String HEADER_LINK = "Link";
#Override
public void process(final Exchange exchange) {
List<String> newLinks= getNewLinks(exchange, oldUrl, newUrl);
ResponseBuilder builder = createHttpResponse(exchange);
builder.header(HEADER_LINK, newLinks);
exchange.getIn().setBody(builder.build());
}
private List<String> getNewLinks(final Exchange exchange, final String oldUrl, final String newUrl) {
ArrayList<String> oldLinks= exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
return oldLinks.stream().map(link-> link.replace(oldUrl, newUrl)).collect(toList());
}
private ResponseBuilder createHttpResponse(final Exchange exchange) {
ResponseBuilder builder = Response.status(getHttpStatusCode(exchange))
.entity(exchange.getIn().getBody());
clearUselessHeader(exchange);
exchange.getIn().getHeaders().forEach(builder::header);
return builder;
}
private void clearUselessHeader(final Exchange exchange) {
exchange.getIn().removeHeader(HEADER_LINK);
exchange.getIn().removeHeaders("Camel*");
}
private Integer getHttpStatusCode(final Exchange exchange) {
return exchange.getIn().getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class);
}
private final String getPropertiesValue(CamelContext camelContext, String key) {
return camelContext.getPropertiesComponent().resolveProperty(key).orElseThrow();
}
}
I work on small test project to check how Spring Reactive Web Applications actually works with MongoDB.
I follow the manual from https://docs.spring.io/spring/docs/5.0.0.M4/spring-framework-reference/html/web-reactive.html
and it states that I can process POST request in controller like:
#PostMapping("/person")
Mono<Void> create(#RequestBody Publisher<Person> personStream) {
return this.repository.save(personStream).then();
}
Though this seems not works. Here the controller I implemented:
https://github.com/pavelmorozov/reactor-poc/blob/master/src/main/java/com/springreactive/poc/controller/BanquetHallController.java
it have just one POST mapping and it is very simple:
#PostMapping("/BanquetHall")
Mono<Void> create(#RequestBody Publisher<BanquetHall> banquetHallStream) {
return banquetHallRepository.insert(banquetHallStream).then();
}
It is called each time I issue a POST with curl:
curl -v -XPOST -H "Content-type: application/json" -d '{"name":"BH22"}' 'http://localhost:8080/BanquetHall'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /BanquetHall HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> Content-type: application/json
> Content-Length: 15
>
* upload completely sent off: 15 out of 15 bytes
< HTTP/1.1 200 OK
< content-length: 0
<
* Connection #0 to host localhost left intact
And I see new objects stored in mongodb, but they not contain data. To debug I build simple subscriber, to see the data actually passed as request body to controller:
Subscriber s = new Subscriber() {
#Override
public void onSubscribe(Subscription s) {
logger.info("Argument: "+s.toString());
}
#Override
public void onNext(Object t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onError(Throwable t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onComplete() {
logger.info("Complete! ");
}
};
banquetHallStream.subscribe(s);
and now I see after subscription onError method called. The Throwable states body missing:
Here error string:
Request body is missing: reactor.core.publisher.Mono<java.lang.Void> com.springreactive.poc.controller.BanquetHallController.create(org.reactivestreams.Publisher<com.springreactive.poc.domain.BanquetHall>)
Why request body is empty?
Also good to know: As I new with all this reactive stuff, could it be some better approach to debug Publisher/Subscriber without manual implementing Subscriber?
Update I updated POST handler method description and it passes request body as String object:
Mono<Void> create(#RequestBody String banquetHallStream)
Then this is not a "Reactive", right? String is not reactive, as Publisher should be...
I had exact the same issue and was able to solve it by putting #ResponseStatus on method. Below is how method controller looks like:
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/bulk", consumes = APPLICATION_STREAM_JSON_VALUE)
public Mono<Void> bulkInsert(#RequestBody Flux<Quote> quotes) {
return quoteReactiveRepository.insert(quotes).then();
}
I'm doing the request to that endpoint using WebClient:
webClient.post()
.uri("/quotes/bulk")
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(flux(), Quote.class)
.retrieve()
.bodyToMono(Void.class).block();
tested with: org.springframework.boot:spring-boot-starter-webflux:2.1.0.RELEASE
I am sending a POST request using the following code but the request is send in the form of chunked (Transfer-Encoding: chunked). I googled about the problem and it says to include the Content-Length but in the below code I could not figure out how could I set the Content-Length:
#RequestMapping(value = "/contacts", method = RequestMethod.POST)
public Map<String, ContactInfo> addContactInfo(
#RequestBody Map<String, ContactInfo> ContactInfoDto) {
ContactInfo contactInfo = ContactInfoDto.get("contact");
if (contactInfo == null) {
throw new IllegalArgumentException("Contact not found.");
}
contactInfo = this.contactInfoManager.addNew(contactInfo);
Map<String, ContactInfo> map = new HashMap<>();
map.put("contact", contactInfo);
return map;
}
You can use ResponseEntity to set headers explicitly. The tricky bit is figuring out how long your content actually is:
#RequestMapping(value = "/contacts", method = RequestMethod.POST)
public ResponseEntity<Map<String, ContactInfo>> addContactInfo(#RequestBody Map<String, ContactInfo> contactInfoDto) throws JsonProcessingException {
ContactInfo contactInfo = contactInfoDto.get("contact");
if (contactInfo == null) {
throw new IllegalArgumentException("Contact not found.");
}
contactInfo = this.contactInfoManager.addNew(contactInfo);
Map<String, ContactInfo> map = new HashMap<>();
map.put("contact", contactInfo);
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_LENGTH, String.valueOf(new ObjectMapper().writeValueAsString(map).length()));
return new ResponseEntity<Map<String, ContactInfo>>(map, headers, HttpStatus.CREATED);
}
Test:
$ curl -v http://localhost:8080/contacts/ -X POST -d '{ "contact": { "name": "foo" } }' -H 'Content-Type: application/json' && echo
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /contacts/ HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 32
>
* upload completely sent off: 32 out of 32 bytes
< HTTP/1.1 201 Created
< Server: Apache-Coyote/1.1
< X-Application-Context: application
< Content-Type: application/json;charset=UTF-8
< Content-Length: 26
< Date: Fri, 10 Jun 2016 13:24:23 GMT
<
* Connection #0 to host localhost left intact
{"contact":{"name":"foo"}}
The following code:
#RequestMapping(value = "/contacts", method = RequestMethod.POST)
public Map<String, ContactInfo> addContactInfo(
#RequestBody Map<String, ContactInfo> ContactInfoDto,
#RequestHeader(value = HttpHeaders.CONTENT_LENGTH, required = true) Long contentLength
) { ... }
Can be used to require Content-Length header to be sent.
Just note that you also have to add that header in code that sends request (most of the clients do that automatically but better check)
I try to make a restfull web service with Jersey. I would like to give a link in the response of that request :
GET /mac/ws/gtm HTTP/1.1
Host: localhost:8080
Accept: application/json
Cache-Control: no-cache
I want the response to be :
HTTP/1.1 200 OK
link: </dossiers>;rel=dossiers
{
"message": "Hello"
}
But the response is :
HTTP/1.1 200 OK
{
"message": "Hello"
}
The link is not produce !
Look my Gtm Resource :
#Component
#Path("/gtm")
public class GTmRessource
{
#GET
#Produces(MediaType.APPLICATION_JSON)
public GTm getJson()
{
GTm gtm = new GTm();
return gtm;
}
}
And my Gtm entity
#XmlRootElement()
#Link(value = #Ref(value = "/dossiers", method = "get"), rel = "dossiers")
public class GTm
{
String message = "Hello";
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
}
What's wrong ?
Thanks for help.
By
See Declarative Hyperlinking: Configuration
You need to add the LinkFilter either programmatically:
resourceConfig.getContainerResponseFilters().add(LinkFilter.class);
or through web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.server.linking.LinkFilter</param-value>
</init-param>
C:\>curl -i http://localhost:8080/gtm
HTTP/1.1 200 OK
Content-Type: application/json
Link: </dossiers>;rel=dossiers
Date: Thu, 04 Dec 2014 12:38:06 GMT
Transfer-Encoding: chunked
{"message":"Hello"}
I generate proxy classes with wsdl.exe to request web-services, that are probably build at java platform. The problem is with encoding of response. I get '?' instead of russian letters.(for example '????26' instead of 'АН26')
I also use soapUI and everything works well there. I am not experienced at configuring .Net clients. So how I could determine and configure proper encoding for response. I already played with app.config as next:
I attach headers information here. I don't wee encoding info at responce headers...
request headers:
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "urn:#DCSSci_ListFlight_5"
Content-Length: 641
Host: 109.73.1.66:23022
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
response headers:
HTTP/1.1 200 OK
Date: Thu, 06 Sep 2012 03:47:52 GMT
Server: Apache/2.2.10 (Linux/SUSE)
200 OKX-FidelXML-Version: 2.0
Content-length: 15464
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/xml
Solution:
public class TraceExtension : SoapExtension
{
Stream oldStream;
Stream newStream;
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type WebServiceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
newStream.Position = 0;
Copy(newStream, oldStream);
break;
case SoapMessageStage.BeforeDeserialize:
message.ContentType = "application/soap+xml; utf-8";
Copy(oldStream, newStream);
newStream.Position = 0;
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from, System.Text.Encoding.GetEncoding("utf-8"));
TextWriter writer = new StreamWriter(to, System.Text.Encoding.GetEncoding("utf-8"));
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
}
And than just add
[TraceExtension()]
attribute for proxy invoke method
You can override GetWebResponse of your proxy and change the encoding
public class YourProxyClass : SoapHttpClientProtocol
{
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
response.Headers["Content-Type"] = "text/xml; charset=utf-8"; //<==
return response;
}
}