Read Body (Payload) of a GET / DELETE in Java play 2.4 - java

I'm using Play 2.4.x framework and I want get the body of a DELETE request. In Play 2.2 this can be simply done using request().body().asJson().toString()
According to the Plat 2.4 migration document, the above method only works for PUT POST and PATCH requests. Not for GET and DELETE. What is the Java code to read the Body of a DELETE request in Play 2.4

In the migration document, they mention that BodyParsers.parse.default has changed its behaviour, but you can still access to the body by specifying an other body parser:
#BodyParser.Of(BodyParser.Json.class)
public Result myAction() {
return ok("Got json: " + request().body().asJson());
}
Note: When using BodyParsers.Json.class, it will automatically verify that the incoming request contains some json and parse it.

Related

Using JEST to write to Elasticsearch 7.3 - invalid POST method

I've been attempting to write some info to a working elasticsearch 7.3 cluster using the JEST api. Some resources:
here
Have run into this error message:
Incorrect HTTP method for uri [/my_index] and method [POST], allowed: [GET, DELETE, PUT, HEAD]
Im sending the data as follows:
// write directly to elastic
Map<String, Object>infoMap = new LinkedHashMap();
lagInfoMap.put("type", "consumer");
lagInfoMap.put("topicval", topic);
lagInfoMap.put("groupval", group);
lagInfoMap.put("sumval", sumLag);
try {
jestResult = jestClient.execute(new Index.Builder(infoMap).index("my_index").build());
if(!jestResult.isSucceeded()) {
LOGGER.error(jestResult.toString());
}
} catch(IOException ioe) {
LOGGER.error("Unable to write to elastic", ioe);
return false;
}
Seems like it's wanting a PUT request but it's not clear from the docs (or any examples I can find) how to modify the execute method to do so.
Some days ago I also had the same problem and finally gave up the idea of using JEST for elasticsearch 7.3, from their Github page, it doesn't look like their latest release which is 6.3.1 https://github.com/searchbox-io/Jest/releases , isn't compitable with elasticsearch 7.X.
Elasticsearch 7.X uses the PUT HTTP method to index a document, while the earlier version used the POST method, hence you get the below exception.
Incorrect HTTP method for uri [/my_index] and method [POST], allowed:
[GET, DELETE, PUT, HEAD]
I would suggest, you use elasticsearch official high level java client, instead of JEST, this is being developed activity by the elastic, company behind the elasticsearch.

CXF file upload service: Couldn't determine the boundary from the message

I am prototyping a very simple POST service to upload a file:
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("fileUpload")
public Response uploadFile(MultipartBody body) {
//never gets to here
System.out.println("In uploadFile");
//TODO: implementation
return Response.ok().build();
}
I get
org.apache.cxf.interceptor.Fault: Couldn't determine the boundary from the message!
I also tried to replace the method declaration with:
public Response uploadFile(List<Attachment> attachments,
#Context HttpServletRequest request) {
as per some Google findings, to no help.
I am using two different clients to invoke this service: chrome://poster, which has a field to include a file attachment, and a simple Python script as is explained here. Both clients produce the same result.
How should I change my service implementation or the call or both in order to be able to pass the CXF validation and enter into the actual body of the service?
REFERENCES:
JAX-RS : Support for Multiparts
Apache CXF: JAX-RS Restful web service for uploading/downloading Text file + Java client
The server side code looks fine. Problem is the way you are sending data from client. You are sending data as a stream in payload not as an attachment which has boundary. To verify quickly you can enable logging request and response by enabling CXF Feature LoggingFeature or Interceptors LoggingInInterceptor and LoggingoutInterceptor. In the log if you see data coming Payload then you are sending data as stream in this case you need to change the way you send the data else you can change consumes to application/octetstream and receive data using inputstream directly.
I'm not aware of the tool you are using, however I use Chrome Extension to postman to test the REST services. If you install the extension and launch the application.
You can upload the file using below approach.
Change Method type to POST from the drop down.
Enter the URL
Select Tab Body
Select Form-Data Radio Button
On the right most row select file from drop down. as shown in diagram.
Choose file to upload.
Optional enter multipart key.
Finally click send.
We can reproduce your error by selecting binary radio button and uploading file as shown below.

How to use Play to return 415 unsupported media type?

It seems that Play 1.x had this functionality, but I can't find an example in the 2.x documentation. Is there a way to configure a controller to return a 415 if accepts or content-type doesn't match? I can do it manually in the controller like this:
// for GET, POST, PUT, etc.
if (!request().accepts(Http.MimeTypes.JSON)) {
return new StatusHeader(UNSUPPORTED_MEDIA_TYPE);
}
// for POST, PUT, PATCH, etc.
if (!request().contentType().orElse("").equals(Http.MimeTypes.JSON)) {
return new StatusHeader(UNSUPPORTED_MEDIA_TYPE);
}
But it seems like this is something that should be supported by the framework w/o having to use that block of code in every controller method.
Play 2.5.x does provide support for doing this.
Apologies that my example is in Scala, but you can achieve just the same with the Java API - just check the docs below.
// Scala example
def accept = Action(parse.json) { implicit request =>
render {
case Accepts.Json() => Ok(Json.parse("""{"message" : "Thanks for sending and accepting JSON"}"""))
}
}
By specifying that you are using an explicit parse.json body parser, Play will automatically build a result with a 415 code for you if the client attempts to send anything other than application/json for the Content-Type. See the Java examples that covers this bit.
The case Accepts.Json() is an extractor that tests that the media type is within range (ie. application/json in your use case). If the client sends a different media type then I think Play builds a 406 Not Acceptable. See the Java examples.

AWS API Gateway and Lambda to return image

Say I have this HTML:
<img src="http://example.com/pic"/>
What I would like to do is have example.com/pic map to an AWS API Gateway endpoint.
That endpoint would then call a lambda function.
That lambda function would read a random image from an s3 bucket and return it.
So my aim is to use a STANDARD HTML image tag and end up with an image from an s3 bucket but going via some decision code in the lambda to decide the image to return.
I know you can use s3 to serve static content directly (hence the lambda to make the decision about what image). I also know I could do stuff in the lambda like b64 encode the response and then handle it on the client but I am aiming to use the standard HTML IMG tag.
Is this possible?
I've tried using the ResponseStreamHandler (Java SDK) for the lambda and returning the byte array of the image and also added the API gateway config to not map the output to JSON, but nothing seems to work!
It seems AWS has simplified this process, so that many answers are outdated and/or overly complicated.
This is how I got Lambda to return an image through the API Gateway, as of June 2018:
1) In API Gateway, enable Use Lambda Proxy integration for your API. (This setting is located on the Integration Request section, where you had set the type to Lambda.)
2) In API Gateway, select your API and click Settings. In Binary Media Types add */*. (Note: I tried adding simply 'image/jpeg', but it seems to require */* to get all of this to work)
3) Be sure to deploy your API, otherwise your changes will not be live. (In API Gateway, select your API, then Actions > Deploy API).
4) In your Lambda code, return your image in Base64 encoding (this example is C# code):
// set the Content-type header
// set to whichever image type you're returning
var headersDic = new Dictionary<string, string>();
headersDic.Add("Content-type", "image/jpeg");
// return the response object that APIGateway requires
return new APIGatewayProxyResponse
{
StatusCode = 200,
Headers = headersDic,
// return the image in Base64 encoding
Body = Convert.ToBase64String(...your image data...),
IsBase64Encoded = true
};
Done.
If you've setup your API to not require authentication, simply type your API link into your browser, and it will display the image. Or put the API link into an IMG tag. e.g. <img src="https://asdf.execute-api.us-east-1.amazonaws.com/live/myapi" />
Note: Even though in step 2 you set the Binary Media Types to */*, API Gateway will still return text if that is what your Lambda is returning.
Luckily, now AWS API Gateway supports binary data, though you also need to update your resource method through the CLI as it is not yet implemented in the Console. This is what you need to do:
In the Method Response of your method
Set Content-Type as image/jpeg in HTTP 200 Status Response
Header
In the Integration Response of your method
Set Content-Type as 'image/jpeg' in Header Mappings. Mind the quotes!
With the AWS CLI, set contentHandling attribute to CONVERT_TO_BINARYon your Integration Response
Check to entire process in this great step-by step guide: https://stackoverflow.com/a/41434295/720665
I've run in to a similar problem. As mentioned you currently can't directly return your image in binary format from your API Gateway endpoint, which would be required for the browser to display it correctly.
However, I solved this by instead having API Gateway return a 302 Redirect, pointing to the correct file in S3. You can have your Lambda function return the url to the file, which is later on mapped to the Location header in API Gateway. The browser will follow the redirect and display the image properly.
There are several ways to implement the redirect, but I did as follow:
Lambda returns an object with the target image like so:
function handler(event, context) {
context.succeed({
location: "https://[bucket-name].s3-eu-west-1.amazonaws.com/myimage.png" });
});
}
Remove the normal '200' Method Response Status from The Integration Response in API Gateway. Replace it with a '302' response status and add the 'Location' header mapped to value 'integration.response.body.location'
Add the 302 status to the Method Response as well
Just to be clear, the client does two different requests:
The first to get the HTML (including the image url).
The second to fetch the image data from the url.
In other words, the image data is not inlined in the HTML.
Based on this knowledge you can have a Lambda (behind the API gateway) as you suggest. The Lambda implementation can have some logic that determines the url to the image stored in S3. However, the Lambda returns JSON data and not HTML (there are workarounds such as return the html in a variable) which makes things trickier, especially for large HTML pages.
I suggest a slightly different approach, since just receiving an image tag will not get you far. I assume you will inline the image tag in a HTML document, probably by using JavaScript. Then you might as well let the API Gateway / Lambda request return a JSON document with the image url and let the JavaScript either update an existing image tag with the new url or generate the tag for you.
It currently isn't possible because you cannot return binary data through the AWS API Gateway.
For this to work, the lambda function would need to return the image data as binary blob, and some meta-information such as the image content type. Then, AWS API Gateway would need to be able to map this to the HTTP response. For example:-
lambda returns:
{
contentType: 'image/png',
image: "encoded binary data"
}
then API gateway would need to map contentType to the 'content-type' header of the response, and put the image data in the body of the response with the right encoding and length.
Unfortunately, it doesn't do this right now. It only maps text encodings like application/json or application/xml as the response type (it is designed for APIs after all).
You could very easily achieve this using ElasticBeanstalk where you have a lot more control over the http response.
I resolved this problem be reconsidering my design. The underlying thinking is that Lambdas are good computing units and bad file servers.
I changed
Client > APIGW > Lambda > Image
into
Client > APIGW > Lambda > SignedURL
then Client(SignedURL) > Image
Since my client was web based, everything was easier after that shift

How to parse serialized json from php web service in java play framework 1.2.x

I need to use some from from a php web service which rendering its data by serializing json in java play framework 1.2.x. What i am doing just using play WS function. and i am getting data from that service. But when I try to get it with JSONObject it throws excepiton which is so normal, because the returned data does not look a json format well. Any body who knows any workarounds or solution would be appreciated.
HttpResponse htp = WS.url("http://www.geoplugin.net/php.gp?ip=78.171.90.49").get();
System.out.println(htp.getContentType()+"\n"+htp.getStatusText()+"\n"+htp.getString());
The returned data :
a:18:{s:17:"geoplugin_request";s:12:"78.171.90.49";s:16:"geoplugin_status";i:200;s:16:"geoplugin_credit";s:145:"Some of the returned data includes GeoLite data created by MaxMind, available from <a href=\'http://www.maxmind.com\'>http://www.maxmind.com</a>.";s:14:"geoplugin_city";s:8:"Istanbul";s:16:"geoplugin_region";s:8:"Istanbul";s:18:"geoplugin_areaCode";s:1:"0";s:17:"geoplugin_dmaCode";s:1:"0";s:21:"geoplugin_countryCode";s:2:"TR";s:21:"geoplugin_countryName";s:6:"Turkey";s:23:"geoplugin_continentCode";s:2:"EU";s:18:"geoplugin_latitude";s:7:"41.0186";s:19:"geoplugin_longitude";s:9:"28.964701";s:20:"geoplugin_regionCode";s:2:"34";s:20:"geoplugin_regionName";s:8:"Istanbul";s:22:"geoplugin_currencyCode";s:3:"TRY";s:24:"geoplugin_currencySymbol";s:15:"YTL";s:29:"geoplugin_currencySymbol_UTF8";s:3:"YTL";s:27:"geoplugin_currencyConverter";s:6:"2.2669";}
You are accessing the PHP endpoint. You need to hit this URL instead:
http://www.geoplugin.net/json.gp?ip=78.171.90.49

Categories