Apache Camel: How to get parameters sent to a Camel Servlet using a HTTP POST request by a smart way? - http

I'm writing a route that may receive a TXT file with some parameters using a Camel Servlet Component.
According to the Camel documentation I'm supposed to have the parameter set in the header of the message.
But in the the case of a HTTP POST it seems that the header is not populated as expected.
I found that the body contains all the request, the file and the parameters.
Here is an example of content :
------WebKitFormBoundaryC9GDMXt2OAHARCZj
Content-Disposition: form-data; name="upfile"; filename="user.txt"
Content-Type: text/plain
hello world...
...
------WebKitFormBoundaryC9GDMXt2OAHARCZj
Content-Disposition: form-data; name="userdata" testtest
------WebKitFormBoundaryC9GDMXt2OAHARCZj
Content-Disposition: form-data; name="id" 12344
------WebKitFormBoundaryC9GDMXt2OAHARCZj--
Does someone know a way to get the parameters by a smart way?
Do I have to make my own parser in my processor?

ericg,
If you use HTTP POST, the parameters are indeed set in the message body. Maybe you should implements a processor in your route to get the parameters from the body and set them in the exchange headers if it's what you need
Best regards,

In the camel we can get HTTP post parameters, I am not sure to get the Content-Disposition. if we want to get Content-Disposition, we can write own processor method.
But sure can get the parameters, your mentioned body content is your uploaded files content.
from(HTTP_INBOUND_URL)
.log("The message : ${in.body}")
.doTry()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// TODO Auto-generated method stub
String templateId = exchange.getIn().getHeader("templateId",
String.class);
}
})
Where templateId is your HTTP post parameter.

Related

Spring 5 - Read JSON or MultipartFile

I ran into what looks like a really simple problem. I have an http endpoint which should accept either JSON body or uploaded file.
Here is definition of controller method:
#PostMapping(value = "/api/endpoint")
public CompletableFuture<ResponseEntity<Void>> createResource(
#RequestParam(name = "file", required = false) MultipartFile file,
#RequestBody(required = false) Command command){
}
Command is a POJO class with Jackson annotations for deserialisation.
When I pass JSON body with Content-Type: application/json, it works fine. But when I pass file with Content-Type: multipart/form-data, I get 415 Unsupported Media Type.
Here is raw http request when passing just JSON body.
POST /api/devices?= HTTP/1.1
Host: localhost:8080
Content-Type: application/json
cache-control: no-cache
{"foo": "bar"}------WebKitFormBoundary7MA4YWxkTrZu0gW--
Any ideas what is causing this, thanks.
Try the below code.
#PostMapping(value = "/api/endpoint",consumes = {"multipart/form-data"})
public CompletableFuture<ResponseEntity<Void>> createResource(
#RequestPart("file") MultipartFile file,
#RequestPart Command command){
}
You might need two separate methods with different parameters and annotations. Which, of course, can each just call a shared internal method for their common behaviors.

Does Spring #RequestBody support the GET method?

I am trying to carry JSON data in an HTTP GET request message, but my Spring MVC server can't seem to retrieve the JSON data from the GET request body.
HTTP's GET method does not include a request body as part of the spec. Spring MVC respects the HTTP specs. Specifically, servers are allowed to discard the body. The request URI should contain everything needed to formulate the response.
If you need a request body, change the request type to POST, which does include the request body.
Based on official info
https://docs.spring.io/spring-framework/docs/4.1.0.RC2/spring-framework-reference/html/mvc.html
#RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));
byte[] requestBody = requestEntity.getBody();
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
In case anyone's here trying to get the OpenAPI generation to treat the fields of the request object as separate GET params, you'll want to use #ParameterObject (org.springdoc.api.annotations.ParameterObject) which was added here: https://github.com/springdoc/springdoc-openapi/issues/590

How to Read the message Body in a HTTP POST in Netty (version >= 4.x)

In my server handler;
- channelRead() always gets the msg as HTTPRequest and there I cannot find any place to get the POST request payload.
Then I tried following in my handler to check if it works. The decoder has 0 elements.
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
In my server pipeline I have HttpServerCodec and a custom handler only.
It's likely that your HTTP request is chunked. You should try to add an io.netty.handler.codec.http.HttpObjectAggregator to your pipeline, just after the codecs. It will give a FullHttpRequest to your handler.
ChannelPipeline p = ...;
...
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("aggregator", new HttpObjectAggregator(1048576));
...
p.addLast("handler", new MyServerHandler());
Alternatively, you could check this example where HttpRequest and HttpContent are handled separately.
As Leo Gomes mentioned, HTTP Request maybe chunked. so add HttpObjectAggregator before your own handler in pipeline.
if HTTP POST request body is Simple Json String. You can parse it in your own handler like this:
private String parseJosnRequest(FullHttpRequest request){
ByteBuf jsonBuf = request.content();
String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8);
return jsonStr;
}

Spring MVC with HttpPutFormContentFilter can't get RequestBody when use PUT method

I have added the HttpPutFormContentFilter in web.xml.
Here is an action for reveiving both GET,POST,DELETE and PUT method.
#RequestMapping(value = "/**")
public ResponseEntity<byte[]> proxy(HttpServletRequest request,#RequestParam MultiValueMap<String, String> params, #RequestBody byte[] body, #RequestHeader MultiValueMap<String, String> headers) {
When I use POST and add the application/x-www-form-urlencoded header,I can get both the request body and request param.
When I use PUT and add the application/x-www-form-urlencoded header,I can get the request param,but I can't get request body!
There is any bug in the HttpPutFormContentFilter?
According to the Servlet specification (see chapters 1.1 and 3.1.1), when you receive a POST request and the content type is application/x-www-form-urlencoded, that form data needs to be made available through the HttpServletRequest#getParameterXXX() methods. This is not true for PUT requests.
In all cases, the body of the HttpServletRequest is available as an InputStream to Servlet and Filter instances.
For POST, when Spring sees
#RequestParam MultiValueMap<String, String> params
it uses a RequestParamMapMethodArgumentResolver to resolve the argument. This reads directly from the HttpServletRequest parameter map. When Spring sees
#RequestBody byte[] body
it uses a RequestResponseBodyMethodProcessor which reads from the HttpServletRequest InputStream using a ByteArrayHttpMessageConverter to fill up a byte[].
Once you've read the HttpServletRequest InputStream, you cannot re-read (in the default configuration).
For PUT, because the Servlet container doesn't store form parameters in the HttpServletRequest for PUT requests, Spring decided to introduce the HttpPutFormContentFilter. This Filter reads the HttpServletRequest body to populate its own parameter map in an HttpServletRequestWrapper which it hands you.
Once this is done, the request parameters are available to the RequestParamMapMethodArgumentResolver, but when the RequestResponseBodyMethodProcessor tries to fill up the byte[], there are no bytes left in the InputStream so it leaves it empty.
One workaround is to create your own Filter (which must execute before the HttpPutFormContentFilter, so it's kind of bad practice), which wraps the HttpServletRequest in a HttpServletRequestWrapper which buffers the InputStream in a ByteArrayInputStream so you can re-read it as many times as necessary.

ASP.NET MVC ignoring Content-Length?

I've been having some problems with missing post data in ASP.NET MVC which has lead me to investigate how ASP.NET MVC deals with invalid content lengths. I had presumed that a post with a invalid content length should be ignored by MVC.NET but this doesn't seem to be the case.
As an example, try creating a new ASP.NET MVC 2 web application and add this action to the HomeController:
public ActionResult Test(int userID, string text)
{
return Content("UserID = " + userID + " Text = " + text);
}
Try creating a simple form that posts to the above action, run fiddler and (using "Request Builder") modify the raw data so that some of the form data is missing (e.g. remove the text parameter). Before executing the request, remember to un-tick the "Fix Content-Length header" checkbox under the Request Builder options then set a break point on the code above and execute the custom http request.
I find that the request takes a lot longer than normal (30 seconds or so) but to my amazement is still processed by the controllers action. Does anyone know if this is expected behavior and, if so, what would you recommend to safeguard against invalid content-lengths?
ASP.NET does not ignore the Content-Length request header. Consider the following controller action as an example which simply echoes back the foo parameter:
[HttpPost]
public ActionResult Index(string foo)
{
return Content(foo, "text/plain");
}
Now let's make a valid POST request to it:
using (var client = new TcpClient("127.0.0.1", 2555))
using (var stream = client.GetStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
{
writer.Write(
#"POST /home/index HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:2555
Content-Length: 10
Connection: close
foo=foobar");
writer.Flush();
Console.WriteLine(reader.ReadToEnd());
}
As expected this prints the response HTTP headers (which are not important) and in the body we have foobar. Now try reducing the Content-Length header of the request:
POST /home/index HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:2555
Content-Length: 5
Connection: close
foo=foobar
Which returns a single f in the response body. So as you can see an invalid HTTP request could lead to incorrect parsing of the parameters.

Resources