Uploading a file using RESTful API based on Grails JAX-RS Plugin - java

I'm trying to get a file with a RESTful API based with JAX-RS on Grails.The file is sent from a regular POST multi-part form with file input tag. ( For sending the file I'm using postman google extention )
But after sending the request I get "HTTP Status 400 - Bad Request" response.
I checked many tutorials and followed exactly their steps but it's not working.
Here is the the code in REST service to handle the request :
import com.sun.jersey.multipart.FormDataParam
import com.sun.jersey.core.header.FormDataContentDisposition;
import org.json.simple.JSONObject
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.ws.rs.Consumes
import javax.ws.rs.FormParam
import javax.ws.rs.GET
import javax.ws.rs.POST
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.Context
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
#Path('/api/upload/')
class UploadResource {
#POST
#Path("/tst")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces('application/json')
public String uploadFile(#FormDataParam("file") InputStream is, #FormDataParam("file") FormDataContentDisposition fileDetail){
String uploadedFileLocation = "Some Location";
// save it
saveToFile(is, uploadedFileLocation);
JSONObject JObject = new JSONObject();
JObject.put("Message", "Aha")
JObject.put("Response", "200")
JObject.put("Status", "OK")
return JObject.toJSONString()
}
}
And here is the way I send the file :
http://postimg.org/image/x3wfrs6h5/

Instead of disabling Grails' multipart resolver completely for the whole application (see: https://code.google.com/p/grails-jaxrs/issues/detail?id=52#c11) you could get the file by accessing the multipart file from Grails' WebUtils Holder.
#POST
#Path("/tst")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces('application/json')
public String uploadFile() {
String uploadedFileLocation = "Some Location";
// here is the workaround for issue: https://code.google.com/p/grails-jaxrs/issues/detail?id=52
GrailsWebRequest request = WebUtils.retrieveGrailsWebRequest()
MultipartFile multipartFile = request.getRequest().getFile('file')
def is = multipartFile.inputStream
// save it
saveToFile(is, uploadedFileLocation);
JSONObject JObject = new JSONObject();
JObject.put("Message", "Aha")
JObject.put("Response", "200")
JObject.put("Status", "OK")
return JObject.toJSONString()
}

The problem occures because the controller initiated by the jaxrs plugin parses the request upfront and this leads to the error.
This problem is known and there is a workaround for this: https://code.google.com/p/grails-jaxrs/issues/detail?id=52#c11
But it is still a known bug on jaxrs plugin.

By default Grails defines a bean named 'multipartResolver' defined for
CommonsMultipartResolver, but this does not work with jax-rs as per the reasons detailed by Denny.
The only issue I see with his proposal to use GrailsWebRequest is that if you go to YOUR_APP_URL/application.wadl, you will not see the file param there and therefore auto-generated client code will not work.
What I have done is override this in resources.groovy to:
multipartResolver(org.springframework.web.multipart.support.StandardServletMultipartResolver) { bean ->
bean.autowire = 'byName'
}

Related

Spring WebFlux File Upload: Unsupported Media Type 415 with Multipart upload

I'm running into some issues handling a file upload using spring's reactive framework. I think I'm following the docs, but can't get away from this 415 / Unsupported Media Type issue.
My controller looks like below (as per the example here: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-multipart-forms)
package com.test.controllers;
import reactor.core.publisher.Flux;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestController {
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<String> uploadHandler(#RequestBody Flux<Part> parts) {
return parts
.filter(part -> part instanceof FilePart)
.ofType(FilePart.class)
.log()
.flatMap(p -> Flux.just(p.filename()));
}
}
POSTing to this endpoint though, always gives me the same output:
curl -X POST -F "data=#basic.ppt" http://localhost:8080/upload
---
"Unsupported Media Type","message":"Content type 'multipart/form-data;boundary=------------------------537139718d79303c;charset=UTF-8' not supported"
I've attempted to use #RequestPart("data") too, but get a similar Unsupported Media Type error, albeit with the content type of the file.
It seems that Spring is having issues converting these to a Part..? I'm stuck - any help is apprecitated!
Well, it's not a direct answer for your question, because I use functional endpoints, but I hope it will help you somehow.
import org.springframework.context.annotation.Bean;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.stereotype.Controller;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import java.io.File;
import java.util.Map;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
#Controller
public class FileUploadController {
#Bean
RouterFunction<ServerResponse> apiRoutes() {
return nest(path("/api"),
route(POST("/upload"), fileUpload()));
}
private HandlerFunction<ServerResponse> fileUpload() {
return request -> {
return request.body(BodyExtractors.toMultipartData()).flatMap(parts -> {
Map<String, Part> map = parts.toSingleValueMap();
final FilePart filePart = (FilePart) map.get("file");
final String dir = "C:\\JDeveloper\\mywork\\Spring\\SpringTest\\webflux-file-upload\\uploaded";
filePart.transferTo(new File(dir + "/" + filePart.filename()));
return ServerResponse.ok().body(fromObject("ok, file uploaded"));
}
);
};
}
}
You can upload a file with curl like this:
curl -F "file=#C:\Users\Wojtek\Desktop\img-5081775796112008742.jpg" localhost:8080/api/fileupload
Thanks #kojot for your answer, but in this case I discovered the issue was my inclusion of spring-webmvc transiently in addition to spring-webflux. Your solution would likely have worked too, but I wanted to stick with the Controller style so ended up forcibly excluding spring-webmvc from my build.gradle:
configurations {
implementation {
exclude group: 'org.springframework', module: 'spring-webmvc'
}
}
After that it worked as documented.

Why Swagger created a systemId field in example?

I have a REST POST function that has the following header:
#POST
#Consumes(value = { MediaType.APPLICATION_JSON + ";charset=utf-8" })
#Produces(value = { MediaType.APPLICATION_JSON + ";charset=utf-8" })
#ApiOperation(value = "Create a document type", notes = "creates a document type from Json and returns the created type", response = Response.class)
#Session(roles = { Role.ROLE_ADMINISTRATOR })
#PublicApi
public Response create(
#ApiParam(value = "Created DocumentType", required = true)
#SwaggerDataType(type =
com.infor.daf.icp.internal.rest.models.DocumentType.class)
com.infor.daf.icp.internal.rest.models.DocumentType documentType) {
When I look at it in Swagger UI, the Swagger creates an example request body. That body has
systemId (string, optional),
in Model view and
systemId : "string"
in the JSON view. But in the whole project there is not a field named systemId. I had checked the request class and its ancestors one by one, and the whole project by search Java. That symbol sequence systemId does not appear even as a substring of another name.
Where does Swagger gets that name and how can I stop it? For I want it to create a valid example, of course.
Edit: The API function itself takes JSON input without problems and correctly composes an object of the declared class.
Imports :
package com....documentarchive.rest.v1
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
The swagger UI looks so:
Edit 2.
I have removed #SwaggerDataType, or replaced it with #RequestBody, but the strange behaviour remains.
I have set the example to be shown as a concrete string with real data:
#ApiParam(example = DOC_TYPE_EXAMPLE, value = "Created DocumentType", required = true) #RequestBody com.infor.daf.icp.internal.rest.models.DocumentType documentType) {
....
static final private String DOC_TYPE_EXAMPLE = "{'entityModel':\n" +
" {'name':'Anatemplate',\n" +
" 'desc':'Ana-template',\n" +
And even that didn't help! Swagger still generates some senseless string from some distant file (thanks to #xpa1492 for the reference) somewhere on the internet, instead of simply showing out the prepared string.
More edit:
The pom file: https://drive.google.com/file/d/16fOCq5EFZYVBJRPg0HeiP102eRzEaH6W/view?usp=sharing
Seems to have been answered here: https://github.com/kongchen/swagger-maven-plugin/issues/608
Swagger configuration was not loading the Jackson annotation module, ignoring all annotations used. Therefore ApiReader was reading wrong class (https://docs.oracle.com/javase/8/docs/api/org/w3c/dom/DocumentType.html).

Accessing request payload in V2 DialogFlow fulfillment webhook

I'm trying to parse the request sent to a java based fulfillment in V2 of the API. I can't find any example documentation in Java for doing this in V2 of the API (com.google.cloud:google-cloud-dialogflow:0.38.0-alpha dependency in my project).
So far I've got as far as writing a very basic Spring MVC controller to accept the request.
How can I parse out the payload in the request, e.g. the parameters that dialog flow sent ?
import com.google.cloud.dialogflow.v2beta1.WebhookRequest;
import com.google.cloud.dialogflow.v2beta1.WebhookResponse;
import com.google.protobuf.Descriptors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;
#RestController
#RequestMapping("test")
public class TestRequestRestController {
private static final Logger log = LoggerFactory.getLogger(TestRequestRestController.class);
#PostMapping("test1t")
public WebhookResponse getTest1(WebhookRequest request) {
System.out.println(request.toString());
return WebhookResponse.newBuilder().setFulfillmentText("Example reply 1 ").build();
}
}
Not sure about WebhookRequest and WebhookResponse.
The code below code might help you.
import org.springframework.http.HttpEntity;
#PostMapping("test1t")
public String getTest1(HttpEntity<String> httpEntity) {
String reqObject = httpEntity.getBody();
System.out.println("request json object = "+reqObject);
//Get the action
JSONObject obj = new JSONObject(reqObject);
String action = obj.getJSONObject("result").getString("action");
//Get the parameters
JSONObject params = obj.getJSONObject("result").getJSONObject("parameters");
String response = "Hello from Java.";
return "{'speech': '"+response+"', 'displayText':'"+response+"'}";
}

Downloading image from Url in java.Server returned HTTP response code: 403 error. How can I get the connection to work?

I am trying to downloada picture from a certain url, but cant do so because I somehow have to give the right userclient to the website.I am sure the problem is that I cant give the user client while using the Url class, because the page can be accesed via browser. I tried using proxy and Urlconnection but couldnt get it to work. Please share your toughts on the matter!
My code is the following:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
import java.net.URLConnection;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
public class KepLetolto {
public static void main(String[] args) throws IOException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File file = new File("tempjpeg");
SocketAddress address = new java.net.InetSocketAddress("xyz.com", 8080);
// Create an HTTP Proxy using the above SocketAddress.
Proxy proxy = new Proxy(Proxy.Type.HTTP, address);
URL url_kep =new URL("http://www.theouthousers.com/images/templates/thumbnails/128058/bayfinger_size3.png");
ImageIO.write(ImageIO.read(url_kep), "jpeg", file);
Mat uj = Highgui.imread("temp.jpeg" ,Highgui.CV_LOAD_IMAGE_COLOR);
}
}
Instead of using ImageIO.read(URL), which limits you to the default behavior of the URL's underlying URLConnection, use ImageIO.read(InputStream).
This allows you to use any HTTP client library - including the basic HttpURLConnection, which you can get from (HttpURLConnection)url_kep.openConnection(). Using that, you can set headers such as User-Agent, if that's the header required by the site, or other headers such as Referer which are sometimes used to prevent deep-linking.
Once you set up all the headers and any other request options, you can get an InputStream from the client object, and pass that to ImageIO.
This Solution Worked For Me:
URLConnection openConnection = new URL("YOUR_IMAGE_URL").openConnection();
openConnection.addRequestProperty("User-Agent", "YOUR USER AGENT");
InputStream is = openConnection.getInputStream();
BufferedImage saveImage = ImageIO.read(is);
ImageIO.write(saveImage, "png", new File("PATH\\TO\\IMAGE\\FILE.PNG"));

How to call put method in REST assured

I have a put method that accepts inputstream. I want to call this method using rest assured in JUnit.
This is what I used:
with().body(inpustream).put("/service/1"); // i got error 404 forbidden.
POST will return status code 201 and PUT will return 200, and POST will create a new resource but, PUT will update the existing resource. This means we will have to mention which resource we wish to update in the URI itself like below.
import io.restassured.RestAssured;
import static io.restassured.RestAssured.*;
import java.util.HashMap;
import java.util.Map;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.*;
public class PUTMethod {
public static Map<String, String> map = new HashMap<String, String>();
#BeforeTest
public void putdata(){
map.put("userId", "2");
map.put("id", "19");
map.put("title", "this is projectdebug.com");
map.put("body", "i am testing REST api with REST-Assured and sending a PUT request.");
RestAssured.baseURI = "http://jsonplaceholder.typicode.com";
RestAssured.basePath = "/posts/";
}
#Test
public void testPUT(){
given()
.contentType("application/json")
.body(map)
.when()
.put("/100")
.then()
.statusCode(200)
.and()
.body("title", equalTo("this is projectdebug.com"));
}
}
Visit http://www.projectdebug.com/send-put-request-using-rest-assured/
for more information.
Actually, you are doing well but sending multipart through PUT is unsecured and is quite random (https://jira.spring.io/browse/SPR-9079). Amend your spring-security.xml to add a filter or use POST method in this case.
You can also try your code by calling another PUT webservice with no stream.
(And what is the error code ? 404 or 403 ?)
A similar problem solved by using MultipartFilter : Spring 3.0 FileUpload only with POST?
Have a look at the following example, where it explains how to use PUT request using Rest Assured:
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static com.jayway.restassured.RestAssured.*;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
public class GetStatusCodeTest {
#BeforeClass
public void setBaseUri () {
RestAssured.baseURI = "https://localhost:3000";
}
#Test
public void updateUsingPut () {
Posts post = new Posts();
post.setId ("3");
post.setTitle ("Hello Bhutan");
post.setAuthor ("StaffWriter");
given().body (post)
.when ()
.contentType (ContentType.JSON)
.put ("/posts/3");
}
}
For detailed explanation, you may check out the following link:
https://restservicestesting.blogspot.in/2016/10/automating-put-request-using-rest.html

Categories