test springboot restcontroller consume json and optional file using postman - json

i have a rest endpoint in my controller like this..
#RequestMapping(value = "/createFoo", method = RequestMethod.POST)
public ResponseEntity<Void> createFoo(#RequestPart(name = "foo") Foo foo,
#RequestPart(name = "file", required = false) MultipartFile file) {
service.createFoo(foo, file);
return new ResponseEntity<>(HttpStatus.CREATED);
}
how can i test this out using postman and curl? i need to send a json foo with or without a file. thanks in advance.

you can shorten it like this #PostMapping("/createFoo")
Firstly specify your method Post and url that postman requested it than
select form-data under Body section
Click key input area and change text to File
In addition parameter name need to be same as form-data variable
Example attached

Related

How to write a test for sending a PDF file to a Spring Boot REST API (application/json) with RestAssured?

I am testing a REST API with RestAssured in Java, and I want to send a PDF file as part of a PUT message. The whole point of this is that I want to add validation to verify that the file being uploaded is an actual PDF, by checking the first two bytes (Magic Bytes).
The API consumes and produces JSON. I have tried printing the file as a JSON string, but that didn't come out right.
I have tried
.header("Content-Type", "multipart/mixed")
.multiPart("file", file, "application/pdf")
But the API will return a 415, as Content-Type needs to be "application/json".
The test looks something like this:
Path pdfPath = Paths.get("src","test","resources", "test.pdf");
File file = pdfPath.toFile();
given()
.header(X_MOCK_ID, mockId.toString())
.header("Content-Type", "application/json")
.body(json)
.when()
.put("/v1/customer/messages/" + messageId)
.then()
.assertThat()
.statusCode(Response.Status.OK.getStatusCode())
.header(HttpHeaders.CACHE_CONTROL, equalTo("no-cache"));
and the controller that serves the request like this:
#PUT
#Consumes("image/jpeg")
#Path("/bild")
#ActionFeatures(JunoService.UpdateProfile)
public void putProfilePicture(#Context HttpServletRequest request,
#Suspended final AsyncResponse response, InputStream payload) {
doInAuthorizedContext(request, response, context -> {
Profile updateProfile = new Profile();
updateProfile.setProfilePicture(toByteArray(payload));
updateProfile.setProfilePictureMimeType("image/jpeg");
profileService.updateProfileImage(context, updateProfile).map(result -> {
return Response.noContent().build();
})
.subscribe(response::resume, response::resume);
});
}
Anyone that has done this before?

Spring WebClient not processing JSON content

I have an app that uses WebClient to fetch JSON data from ComicVine as follows:
WebClient client = WebClient.builder()
.baseUrl(url)
.defaultHeaders(
headers -> {
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
headers.add(HttpHeaders.USER_AGENT, "ComiXed/0.7");
})
.build();
Mono<ComicVineIssuesQueryResponse> request =
client
.get()
.uri(url)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(ComicVineIssuesQueryResponse.class);
ComicVineIssuesQueryResponse response = request.block();
For a time this worked. But then, all of a sudden, it's throwing the following root exception when it executes:
Caused by: org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/json' not supported for bodyType=org.comixed.scrapers.comicvine.model.ComicVineIssuesQueryResponse
at org.springframework.web.reactive.function.BodyExtractors.lambda$readWithMessageReaders$12(BodyExtractors.java:201)
I'm not sure why it all of a sudden won't process JSON data. My unit test, which is explicitly returning JSON data and setting the content type properly:
private MockWebServer comicVineServer;
this.comicVineServer.enqueue(
new MockResponse()
.setBody(TEST_GOOD_BODY)
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));
Any ideas why this is the case? It's happening across multiple classes that use this same setup for WebClient and for testing.
After doing some digging, I added the following code to get the JSON as a String and then use ObjectMapper to convert it to the target type:
Mono<String> request =
client
.get()
.uri(url)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class);
String value = request.block();
ObjectMapper mapper = new ObjectMapper();
ComicVineIssuesQueryResponse response = mapper.readValue(value, ComicVineIssuesQueryResponse.class);
This quickly exposed the underlying problem, which was that two instance variables in the response were annotated with the same JSON field name. Once I fixed that, things started working correctly again.
you can parse the json content to string without calling block method.
option 1) Jackson2Tokenizer
option 2) put your code which is calling "objectMapper.readValue(..) .." inside map operator.

.NET Core - How to upload JSON file?

I am trying to upload JSON file in order to read values from it and save them in database, but I have problem with that. Code of my controller looks as following:
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class ImportController : ControllerBase
{
private readonly DatabaseContext dbContext;
public ImportController(DatabaseContext dbContext)
{
this.dbContext = dbContext;
}
[HttpPost]
public IActionResult ImportData(IFormFile file)
{
var content = string.Empty;
using (var reader = new StreamReader(file.OpenReadStream()))
{
content = reader.ReadToEnd();
}
List<UserModel> userObjects = null;
try
{
userObjects = JsonConvert.DeserializeObject<List<UserModel>>(content);
}
catch
{
return BadRequest();
}
foreach (var user in userObjects)
{
UserModel us = new UserModel
{
Username = user.Username,
Password = user.Password
};
dbContext.User.Add(us);
dbContext.SaveChanges();
}
return Ok();
}
}
I'm using Postman to send JSON data, but anytime I try to do it, I get following response:
{"Username":["The input was not valid."]}
when I try to send JSON data as raw->application/json OR
{"":["The input was not valid."]}
when I try to send it by form-data with key called "file" and test.json file as value.
Could you direct me to the right path? I tried to use [FromBody] UserModel user as parameter of my action, but it only allows me to process one JSON string.
You can use [FromBody] IEnumerable<UserModel> users to process many rows. In this case json should look like:
[
{
"userName": "name",
"password": "password",
},
{
"userName": "name1",
"password": "password1",
}
]
You need to standardize your approach one way or another. If you want to accept JSON, then bind to an action param of type List<UserViewModel> with the [FromBody] attribute, and client-side, use JavaScript's FileReader to get the actual content of the upload loaded file and post the content, rather than the file.
If you want to do it by file upload, then you can keep the action as it is, but you'll need to then send your own "JSON" as a file upload as well. This can be achieved by using FormData in JavaScript and creating a Blob manually from your JSON object as a string.
Long and short, whichever path you choose, be uniform about it. There's no way to handle both posting a JSON object and a file upload that happens to be a text file with a .json extension in the same action.
I resolved it... All I had to do was deleting [ApiController] attribute. Having that attribute caused application to didn't visit my ImportData method at all.

Grails send request as JSON and parse it in controller

I want to send a request as JSON and in my controller I want to parse this JSON and get the parameters I want. for example this is the request:
{"param1":"val1"}
I want to parse this request and get "param1" value. I used request.JSON but still I got null. Is there any other way to solve this?
Thanks,
You can use one of the following to test your stuff (both options could be re-used as automated tests eventually - unit and integration):
write a unit test for you controller like (no need to start the server):
void testConsume() {
request.json = '{param1: "val1"}'
controller.consume()
assert response.text == "val1"
}
and let's say your controller.consume() does something like:
def consume() {
render request.JSON.param1
}
Or you can use for example the Jersey Client to do a call against your controller, deployed this time:
public void testRequest() {
// init the client
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
// create a resource
WebResource service = client.resource(UriBuilder.fromUri("your request url").build());
// set content type and do a POST, which will accept a text/plain response as well
service.type(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_PLAIN).put(Foo.class, foo);
}
, where foo is a Foo like this:
#XmlRootElement
public class Foo {
#XmlElement(name = "param1")
String param1;
public Foo(String val){param1 = val;}
}
Here are some more examples on how to use the Jersey client for various REST requests:
https://github.com/tavibolog/TodaySoftMag/blob/master/src/test/java/com/todaysoftmag/examples/rest/BookServiceTest.java
Set it in your UrlMappings like this:
static mappings = {
"/rest/myAction" (controller: "myController", action: "myAction", parseRequest: true)
}
Search for parseRequest in latest Grails guide.
Then validate if it works correctly with curl:
curl --data '{"param1":"value1"}' --header "Content-Type: application/json" http://yourhost:8080/rest/myAction
In the controller method, check request.format. It should specify json. I'm guessing it won't here, but it may give you clues as to how your payload is being interpreted.
In your Config.groovy file, I would set the following values:
grails.mime.file.extensions = false
grails.mime.use.accept.header = false
In that same file, check your grails.mime.types. make sure it includes json: ['application/json', 'text/json'], which it probably will, but put it above */*. These entries are evaluated in order (this was true in pre 2.1 versions, havent' verified it is now, but what the heck). In conjunction with that, as aiolos mentioned, set your content-type header to one of the above mime-types.
Finally, test with curl, per Tomasz KalkosiƄski, or, to use RESTClient for FF, click on "Headers" in the very top of the client page (there are 4 clickable items at the top-left; headers is one. From a fresh RESTClient, you may have to choose "Custom Header". I can't recall)

Posting a File and Associated Data to a RESTful WebService preferably as JSON

In an application I am developing RESTful API and we want the client to send data as JSON. Part of this application requires the client to upload a file (usually an image) as well as information about the image.
I'm having a hard time tracking down how this happens in a single request. Is it possible to Base64 the file data into a JSON string? Am I going to need to perform 2 posts to the server? Should I not be using JSON for this?
As a side note, we're using Grails on the backend and these services are accessed by native mobile clients (iPhone, Android, etc), if any of that makes a difference.
I asked a similar question here:
How do I upload a file with metadata using a REST web service?
You basically have three choices:
Base64 encode the file, at the expense of increasing the data size by around 33%, and add processing overhead in both the server and the client for encoding/decoding.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
You can send the file and data over in one request using the multipart/form-data content type:
In many applications, it is possible for a user to be presented with
a form. The user will fill out the form, including information that
is typed, generated by user input, or included from files that the
user has selected. When the form is filled out, the data from the
form is sent from the user to the receiving application.
The definition of MultiPart/Form-Data is derived from one of those
applications...
From http://www.faqs.org/rfcs/rfc2388.html:
"multipart/form-data" contains a series of parts. Each part is
expected to contain a content-disposition header [RFC 2183] where the
disposition type is "form-data", and where the disposition contains
an (additional) parameter of "name", where the value of that
parameter is the original field name in the form. For example, a part
might contain a header:
Content-Disposition: form-data; name="user"
with the value corresponding to the entry of the "user" field.
You can include file information or field information within each section between boundaries. I've successfully implemented a RESTful service that required the user to submit both data and a form, and multipart/form-data worked perfectly. The service was built using Java/Spring, and the client was using C#, so unfortunately I don't have any Grails examples to give you concerning how to set up the service. You don't need to use JSON in this case since each "form-data" section provides you a place to specify the name of the parameter and its value.
The good thing about using multipart/form-data is that you're using HTTP-defined headers, so you're sticking with the REST philosophy of using existing HTTP tools to create your service.
I know that this thread is quite old, however, I am missing here one option. If you have metadata (in any format) that you want to send along with the data to upload, you can make a single multipart/related request.
The Multipart/Related media type is intended for compound objects consisting of several inter-related body parts.
You can check RFC 2387 specification for more in-depth details.
Basically each part of such a request can have content with different type and all parts are somehow related (e.g. an image and it metadata). The parts are identified by a boundary string, and the final boundary string is followed by two hyphens.
Example:
POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]
--xyz
Content-Type: application/json; charset=UTF-8
{
"name": "Sample image",
"desc": "...",
...
}
--xyz
Content-Type: image/jpeg
[image data]
[image data]
[image data]
...
--foo_bar_baz--
Here is my approach API (i use example) - as you can see, you I don't use any file_id (uploaded file identifier to the server) in API:
Create photo object on server:
POST: /projects/{project_id}/photos
body: { name: "some_schema.jpg", comment: "blah"}
response: photo_id
Upload file (note that file is in singular form because it is only one per photo):
POST: /projects/{project_id}/photos/{photo_id}/file
body: file to upload
response: -
And then for instance:
Read photos list
GET: /projects/{project_id}/photos
response: [ photo, photo, photo, ... ] (array of objects)
Read some photo details
GET: /projects/{project_id}/photos/{photo_id}
response: { id: 666, name: 'some_schema.jpg', comment:'blah'} (photo object)
Read photo file
GET: /projects/{project_id}/photos/{photo_id}/file
response: file content
So the conclusion is that, first you create an object (photo) by POST, and then you send second request with the file (again POST). To not have problems with CACHE in this approach we assume that we can only delete old photos and add new - no update binary photo files (because new binary file is in fact... NEW photo). However if you need to be able to update binary files and cache them, then in point 4 return also fileId and change 5 to GET: /projects/{project_id}/photos/{photo_id}/files/{fileId}.
I know this question is old, but in the last days I had searched whole web to solution this same question. I have grails REST webservices and iPhone Client that send pictures, title and description.
I don't know if my approach is the best, but is so easy and simple.
I take a picture using the UIImagePickerController and send to server the NSData using the header tags of request to send the picture's data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myServerAddress"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"myPhotoTitle" forHTTPHeaderField:#"Photo-Title"];
[request setValue:#"myPhotoDescription" forHTTPHeaderField:#"Photo-Description"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
At the server side, I receive the photo using the code:
InputStream is = request.inputStream
def receivedPhotoFile = (IOUtils.toByteArray(is))
def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"
if (photo.save()) {
File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
saveLocation.mkdirs()
File tempFile = File.createTempFile("photo", ".jpg", saveLocation)
photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()
tempFile.append(photo.photoFile);
} else {
println("Error")
}
I don't know if I have problems in future, but now is working fine in production environment.
FormData Objects: Upload Files Using Ajax
XMLHttpRequest Level 2 adds support for the new FormData interface.
FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest send() method.
function AjaxFileUpload() {
var file = document.getElementById("files");
//var file = fileInput;
var fd = new FormData();
fd.append("imageFileData", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", '/ws/fileUpload.do');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
alert('success');
}
else if (uploadResult == 'success')
alert('error');
};
xhr.send(fd);
}
https://developer.mozilla.org/en-US/docs/Web/API/FormData
Since the only missing example is the ANDROID example, I'll add it.
This technique uses a custom AsyncTask that should be declared inside your Activity class.
private class UploadFile extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
// set a status bar or show a dialog to the user here
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
// progress[0] is the current status (e.g. 10%)
// here you can update the user interface with the current status
}
#Override
protected String doInBackground(Void... params) {
return uploadFile();
}
private String uploadFile() {
String responseString = null;
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://example.com/upload-file");
try {
AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
new ProgressListener() {
#Override
public void transferred(long num) {
// this trigger the progressUpdate event
publishProgress((int) ((num / (float) totalSize) * 100));
}
});
File myFile = new File("/my/image/path/example.jpg");
ampEntity.addPart("fileFieldName", new FileBody(myFile));
totalSize = ampEntity.getContentLength();
httpPost.setEntity(ampEntity);
// Making server call
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == 200) {
responseString = EntityUtils.toString(httpEntity);
} else {
responseString = "Error, http status: "
+ statusCode;
}
} catch (Exception e) {
responseString = e.getMessage();
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
// if you want update the user interface with upload result
super.onPostExecute(result);
}
}
So, when you want to upload your file just call:
new UploadFile().execute();
I wanted send some strings to backend server. I didnt use json with multipart, I have used request params.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void uploadFile(HttpServletRequest request,
HttpServletResponse response, #RequestParam("uuid") String uuid,
#RequestParam("type") DocType type,
#RequestParam("file") MultipartFile uploadfile)
Url would look like
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
I am passing two params (uuid and type) along with file upload.
Hope this will help who don't have the complex json data to send.
You could try using https://square.github.io/okhttp/ library.
You can set the request body to multipart and then add the file and json objects separately like so:
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploadFile", uploadFile.getName(), okhttp3.RequestBody.create(uploadFile, MediaType.parse("image/png")))
.addFormDataPart("file metadata", json)
.build();
Request request = new Request.Builder()
.url("https://uploadurl.com/uploadFile")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
logger.info(response.body().string());
#RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
public #ResponseBody Object jsongStrImage(#RequestParam(value="image") MultipartFile image, #RequestParam String jsonStr) {
-- use com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}
Please ensure that you have following import. Ofcourse other standard imports
import org.springframework.core.io.FileSystemResource
void uploadzipFiles(String token) {
RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
def zipFile = new File("testdata.zip")
def Id = "001G00000"
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
form.add("id", id)
form.add('file',new FileSystemResource(zipFile))
def urld ='''http://URL''';
def resp = rest.post(urld) {
header('X-Auth-Token', clientSecret)
contentType "multipart/form-data"
body(form)
}
println "resp::"+resp
println "resp::"+resp.text
println "resp::"+resp.headers
println "resp::"+resp.body
println "resp::"+resp.status
}