Unable to upload file via REST request in GRAILS - rest

I am trying to upload a file via REST using GRAILS
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: d5d7aef8-3964-311b-8b64-4a7a82c52323" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "file1=myfile.jpg" -F "fname=swateek" -F "lname=jena" 'http://localhost:8081/sampleFileREST/document/upload'
Here's how my controller looks like:
class DocumentController extends RestfulController<Document> {
static responseFormats = ['json', 'xml']
def upload() {
def fileLocation="<xml>empty</xml>"
def file = request.getParameter('file1')
def f1 = request.getParameter('fname')
def f2 = "<abc>"+request.getParameter('lname')+"</abc>"
def params = "gf"
if(file.empty) {
fileLocation = "<xml>"+"File cannot be empty"+"</xml><allprm>"+params+"</allprm>"
} else {
def documentInstance = new Document()
documentInstance.filename = file.originalFilename
documentInstance.fullPath = grailsApplication.config.uploadFolder + documentInstance.filename
file.transferTo(new File(documentInstance.fullPath))
documentInstance.save()
fileLocation = "<xml>"+documentInstance.fullPath+"</xml>"
}
/* return "File uploaded to: "+documentInstance.fullPath */
render(text: fileLocation, contentType: "text/xml", encoding: "UTF-8")
}
}
I am able to access the parameters of the request, anything except the file I am sending in the request.
Unable to figure out what's wrong here.
UPDATE
I had used .getParameter() to fetch a file. That's incorrect, the correct way is as below:
request.getFile('<filename>') // without the <>
This might raise an error in IntelliJ as "Symbol Not Found" or "Cannot Resolve Method", please follow the procedure in the answer below.

Damn the IDE that I was using, IntelliJ.
Also, this piece of code while getting the file:
def file = request.getParameter('file1')
should be replaced as
def file = request.getFile('file1')
Now previously, when I was using the request.getFile() method I was getting an "Symbol Not Found" error and it was failing to execute the request.
Solution:
Open IntelliJ
Click on "File"
Find the option "Invalidate Caches/Restart" and wait for IntelliJ to come back again.
If this doesn't work, the other way is mentioned in this answer:
IntelliJ IDEA JDK configuration on Mac OS

Related

SoapUI POST to REST with attached file in Groovy

I'm trying to POST to a Sharepoint REST service an attached file with SoapUI Pro. I've tried the examples at: https://support.smartbear.com/readyapi/docs/requests/attachment/rest.html
But with no luck.
It should work with POST with byte-array as body. But how do I do that in SoapUI and Groovy?
In the tool Insomnia it works with "Binary File".
I add these headers:
Accept: application/json;odata=verbose
Content-Type: application/octet-stream
Media type = multipart/mixed and Post QueryString
But the file won't be uploaded to SharePoint.
PowerShell code that works:
$headers = #{
'X-RequestDigest' = 'xxxxxxxxxxxxxxxxxxxxxxx'
'Accept' = 'application/json;odata=verbose'
}
$document = [System.IO.File]::ReadAllBytes('C:\temp\myFile.docx')
Invoke-RestMethod -Method Post -UseDefaultCredentials -Uri "https://xxxx.xxx/add(url='myFile.docx',%20overwrite=true)" -Headers $headers -Body $document
I tried to go through this as well a while ago but I found it easier to use HTTP to do this.
You may try to see if it fits your requirements
My groovy script for attachment :
// upload source file before import
// get uploading request
def source_file = context.expand( '${#TestCase#source_file_path}' )
log.info "upload $source_file"
def aPIToolsTestSuite = context.expand( '${#Project#APIToolsTestSuite}' ) // the test suite that contains the test case with the HTTP request
tc_name = "import - upload resource files"
request = testRunner.testCase.testSuite.project.testSuites[aPIToolsTestSuite].testCases[tc_name].getTestStepByName("Request 1").testRequest
// clear request from any existing attachment
for (a in request.attachments)
{
request.removeAttachment(a)
}
// attach file to upload
def file = new File(source_file)
if (file == null)
{
log.error "bad file name : $source_file"
}
else
{
// attach file and set properties
try{
def attachment = request.attachFile (file, true)
attachment.contentType = "application/octet-stream"
attachment.setPart("upload file '$source_file'")
}
catch (Exception e){
log.error "file ${file.name} : exception $e"
}
}
// now upload file - launch the request
def jsonSlurper = new groovy.json.JsonSlurper()
def TC;
def async = false
TC = testRunner.testCase.testSuite.project.getTestSuiteByName(aPIToolsTestSuite).getTestCaseByName(tc_name)
result = TC.run (context.getProperties(), async)
if (String.valueOf( result.status ) != "PASS")
{
msg = "unexpected failure during $tc_name when uploading $source_file"
testRunner.fail(msg)
log.error msg
}
else
{
// this part is for further processing
// file uploaded, go through the import and properties backup process
resource_to_import = TC.getPropertyValue("testResponse").split('\"')[1]
// file uploaded, go through the import and properties backup process
testRunner.testCase.setPropertyValue("resource_id", resource_to_import)
}
And the HTTP request contained in the test case APIToolsTestSuite/import - upload resource files
first step : get endpoint
def env = testRunner.testCase.testSuite.project.activeEnvironment
rest = env.getRestServiceAt(0)
config = rest.getEndpoint().config
endpoint = new XmlSlurper().parseText(config.toString())
testRunner.testCase.setPropertyValue("endpoint", endpoint.toString())
second step, HTTP request:
POST
with Request tab parameters :
name : metadata
value : {"storageType":"FILESYSTEM","itemName":"my_source_file"}
type : QUERY
media type : multipart/form-data
Post QueryString
Headers : application/json
Good luck :)

How do I upload an image to Parse Server using Kotlin/Jvm via Rest Service?

I'm creating a Kotlin/Jvm (without Android Sdk) application that interacts with a instance of a Parse Server (Back4App). Unfortunately, parse doesn't provide a Sdk implementation to use with Java/Kotlin without Android.
So I'm using the rest Api. Now I trying to upload a image from my disk into Back4App file server. In the doc there is snippet using curl. But I wasn't able to translate into a Retrofit service:
curl -X POST \
-H "X-Parse-Application-Id: 4MGgDJ0ZiQloXoSTE2I9VM6YUYIz8EwCKF4pK7zr" \
-H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
-H "Content-Type: image/jpeg" \
--data-binary '#myPicture.jpg' \
https://YOUR.PARSE-SERVER.HERE/parse/files/pic.jpg
So I based my implementation in this article and other snippets from GitHub and created a retrofit service for it:
#Multipart
#POST("/parse/files")
fun upload(
#Part file: MultipartBody.Part
): Call<ResponseBody>
And call:
var file = File("assets/escudo.png")
var requestFile = RequestBody.create(MediaType.parse("**/image"), file)
var body = MultipartBody.Part.createFormData("picture", file.name, requestFile)
var r = getService().upload(body).execute()
I created the retrofit instance as below:
fun getService(): ParserService {
val retrofit = Retrofit
.Builder()
.baseUrl("https://parseapi.back4app.com")
.addConverterFactory(GsonConverterFactory.create())
.client(createClient()).build()
return retrofit.create(ParserService::class.java)
}
fun createClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHeadInterceptor()).build()
}
fun createHeadInterceptor(): Interceptor {
return HeaderInterceptor()
}
class HeaderInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response =
chain.run {
val credentials = CredentialsUtils.readCredentials()
log.info { credentials }
proceed(
request().newBuilder()
// .addHeader("Content-Type", "application/json")
.addHeader("Content-Type", "image/png")
.addHeader("X-Parse-Application-Id", credentials.back4appAppId)
.addHeader("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
.build()
)
}
}
I was able to use it to posting Json data (by uncommenting the content/type header). But when I tried to upload an image I receive this response:
Response{protocol=h2, code=400, message=, url=https://parseapi.back4app.com/parse/files}
More info:
-- EDIT
I tried a different approuch without Retrofit, it gives a 201 response code and gives me an objectId, but it doesn't upload the file:
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val url = URL(serverUrl)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.doOutput = true
val postData = file2.readBytes()
conn.addRequestProperty("Content-length", postData.size.toString())
conn.setRequestProperty("Content-Type", "image/*")
conn.setRequestProperty("X-Parse-Application-Id", credentials.back4appAppId)
conn.setRequestProperty("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
val outputStream = DataOutputStream(conn.outputStream)
outputStream.write(postData)
outputStream.flush()
println(conn.responseCode)
-- EDIT
Trying now using Khttp:
val file = File("assets/foto.jpg")
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val files = listOf(FileLike("foto.jpg", file), FileLike("escudo.png", file2))
val response = post(serverUrl, headers = getHeaders(), files = files)
println(response)
println(response.text)
}
fun getHeaders(): Map<String, String> {
return mapOf(
"Content-Type" to "image/*",
"X-Parse-Application-Id" to credentials.back4appAppId,
"X-Parse-REST-API-Key" to credentials.back4appRestApiKey
)
}
Getting this error:
<Response [400]>
{"error":"Unexpected token - in JSON at position 0"}
If you're using Back4App, the correct Server URL is:
https://parseapi.back4app.com/files/pic.jpg

Translating curl into Matlab/Webwrite

I have the following curl command I need to sent to a web server using Matlab and webwrite using POST. My problem is that I always get a "Bad request" answer so my syntax must be wrong somehow. Does anybody have an idea how this curl command, sending the body could look like in Matlab using webwrite in a correct way ?
body=$(cat << EOF
{
"order": {
"units": "100",
"instrument": "EUR_USD",
"timeInForce": "FOK",
"type": "MARKET",
"positionFill": "DEFAULT"
}
}
EOF
)
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <AUTHENTICATION TOKEN>" \
-d "$body" \
"https://api-fxtrade.oanda.com/v3/accounts/<ACCOUNT>/orders"
I have just asked a potentially similar question so this may not work first time. However I cannot test without knowing some login details so I can but hope this helps.
data_InputValues = struct ('units',100,'instrument','EUR_USD','timeInForce','FOK','type','MARKET','positionFill','DEFAULT');
MyBody = matlab.net.http.MessageBody(struct('order',data_InputValues));
MyHTTPOptions = matlab.net.http.HTTPOptions(); % use this to change the options if necessary (e.g. extend timeout)
Request = matlab.net.http.RequestMessage;
Request.Method = 'POST';
Request.Header = matlab.net.http.HeaderField('Content-Type','application/json','Authorization: Bearer',AUTHENTICATION TOKEN);
Request.Body = MyBody;
uri = matlab.net.URI('https://api-fxtrade.oanda.com/v3/accounts/<ACCOUNT>/orders');
[response a ~] = Request.send(uri,MyHTTPOptions);
The part I struggle with is generating the MyBody part (in your case this is parsing the order variable's sub-variables). If you get this to work I would be keen to know how! P.S. my question in case it helps: Matlab RESTful PUT Command - net.http - nesting body values
The correct format for the body is as follows:
body = struct('units',100,'instrument','EUR_USD','timeInForce','FOK',...
'type','MARKET','positionFill','DEFAULT');
As for the HTTP headers that you require you can specify them with weboptions when using webwrite.
The syntax for an additional header:
options = weboptions('KeyName','Name','KeyValue','Value')
Where Name and Value are the name of the header and its value respectively.
You must add the headers that you require in weboptions.
For the code you provided, the correct syntax would be as follows:
options = weboptions('MediaType','application/json',...
'KeyName','Authorization: Bearer','KeyValue','Token');
You can then perform the POST request at the URL of interest.
response = webwrite(url,body,options);

Matlab RESTful PUT Command - net.http - nesting body values

I am using Matlab's matlab.net.http library to launch get, put and post commands to a website. I can successfully launch get and post commands.
For example:
MyBody = matlab.net.http.MessageBody(struct('Id',YYYYYY,'WindfarmId',XXX,'Month','YYYY-MM-DD'));
Request = matlab.net.http.RequestMessage;
Request.Method = 'POST';
Request.Header = matlab.net.http.HeaderField('Content-Type','application/json','Authorization',['Basic ' matlab.net.base64encode([Username ':' Password])]);
Request.Body = MyBody;
uri = matlab.net.URI(ENTERURLHERE);
Response = Request.send(uri,MyHTTPOptions);
This works well. However using a PUT command I have to enter the equiavlent of this body (written in curl syntax):
-d '{ "InputValues": [ {"MetricLevelAId": 1, "MetricLevelBId": 1, "InputMetricId": 7, "Value": 56 } ] }'
I tried this:
data_InputValues = struct ('MetricLevelAId',1,'MetricLevelBId',1,'InputMetricId',7,'Value',56);
MyBody = matlab.net.http.MessageBody(struct('InputValues',dataInputValues));
However I keep receiving the following 'Bad Request' response from the server:
"Input values required"
I think this is linked to the way Matlab interprets the body part of the request and passes it to the server, i.e. it cannot pass the nested struct correctly. Anyone got any ideas how to solve this?
N.B. potentially linked to Translating curl into Matlab/Webwrite (it is dealing with a nested value)

Creating command line strings with groovy for cURL - cURL ignores options

I need help figuring out why the last two parameters of my cURL query are ignored.
Please refrain on comment on how this is not the best way to do a rest call. I KNOW. This is going to be a kind of fall back method / work around for another issue.
I manyl handle my rest-work with the wslite (1.1.2) API.
Now let me explain what i do:
I am using the groovy shell executor to make a command line call for a rest service via cURL.
I have built a little class to build the query string and handle the command line:
class Curl {
def static getUserLogin(){
def url = '"https://some-login.someSystem-dev.someHost.com/someResource.beyond.foobar/login/LoginAUser '
def requestFilePath = '-d #temp/LoginPayload.json '
def heads = "-H 'Content-Type: application/json' -H 'Accept: text/plain' "
def params = '-k -v' //-k = ignore unsecure -v = more verbose output
def fullurl = url+requestFilePath+heads+params
return ex(fullurl)
}
/**
*
* #param _command The command you want to execute on your shell.
* #param _workingDir Optional: You may specify the directory where the command will be executed. Default is user dir.
* #return Exit value for the process. 0 = normal termination.
*/
def static ex(String _command, File _workingDir = new File(System.properties.'user.dir')) {
println "Executing command> $_command \n"
def process = new ProcessBuilder(addShellPrefix(_command))
.directory(_workingDir)
.redirectErrorStream(true)
.start()
process.inputStream.eachLine {println it}
process.waitFor();
return process.exitValue().value
}
private static addShellPrefix(String _command) {
def commandArray = new String[2]
commandArray[0] = "curl "
commandArray[1] = _command
return commandArray
}
}
Curl.getUserLogin() //to execute
I hope the code is self-explenatory enough. It all works fine with simple URLs respectively with less parameters.
Executing this will yield the following response (excerpt from the full debug output):
Executing command>
"https://some-login.someSystem-dev.someHost.com/someResource.beyond.foobar/login/LoginAUser"
-d #temp/LoginPayload.json -H 'Content-Type: application/json' -H 'Accept: text/plain' -k -v
% Total % Received % Xferd Average Speed Time Time Time
Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:--
--:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 curl: (60) SSL certificate problem: self signed certificate in certificate chain More details here:
http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a
"bundle" of Certificate Authority (CA) public keys (CA certs). If the
default bundle file isn't adequate, you can specify an alternate file
using the --cacert option. If this HTTPS server uses a certificate
signed by a CA represented in the bundle, the certificate
verification probably failed due to a problem with the certificate
(it might be expired, or the name might not match the domain name in
the URL). If you'd like to turn off curl's verification of the
certificate, use the -k (or --insecure) option.
Now, as you can see I have attached the required option "-k" to the query string but somehow it is ignored. Using this string directly in the windows command line tool (if you try this make sure you escape potential double quotes) works perfectly fine though.
Any ideas why this happens or how I could accquire more debug information?
Thx in advance!
UPDATE:
Solution:
Passing ever option as a single argument (via a list) fixed the issue.
New Issue:
After that i wante curl to output the response to a file using '-o C:\Temp\response.txt' to the argument list. This works fine when used from a command line tool. Executing it from the groovy script results in:
curl: (23) Failed writing body (0 != 386)
I can get around this by just writing the stream to a file. What is really bugging me is that fact that the response does not seem to contain any information in the body. Executing the curl command from windows command line tool returns me a pretty long token as expected.
Andy ideas?
If you use ProcessBuilder, you have to give each parameter as own argument. You give two arguments to the constructor, the program name and the remaining parameters which are taken as one argument, just like if you put quotes around the whole string in the command line. Make fullurl a list instead where each parameter is its own list element and it should work as expected. You can and should leave out any other quoting like you have around the URL though.
Your code can be greatly improved. You shouldn't concatenate the command parts into a single String, just use a List.
Also, the _ prefix on variables is commonly used for private fields or just internals, not method parameters which are clearly not internals.
Using String arrays in Groovy is quite strange, you should definitely learn some Groovy!
Anyways, here's a better version of this code:
def static getUserLogin() {
def url = '"https://some-login.someSystem-dev.someHost.com/someResource.beyond.foobar/login/LoginAUser'
def requestFilePath = '-d #temp/LoginPayload.json'
def heads = "-H 'Content-Type: application/json' -H 'Accept: text/plain' "
def insecure = '-k'
def verbose = '-v'
return ex( [ url, requestFilePath, heads, insecure, verbose ] )
}
/**
*
* #param commands The command + args you want to execute on your shell.
* #param _workingDir Optional: You may specify the directory where the command will be executed. Default is user dir.
* #return Exit value for the process. 0 = normal termination.
*/
static ex( List<String> commands, File _workingDir = new File( System.properties.'user.dir' ) ) {
println "Executing command> $commands \n"
def process = new ProcessBuilder( addShellPrefix( commands ) )
.directory( _workingDir )
.inheritIO()
.start()
process.waitFor()
return process.exitValue().value
}
private static addShellPrefix( List<String> commands ) {
[ 'curl' ] + commands
}