I've used the template yesod-simple to create a scaffold.
The handler Home.hs has these lines:
-- Define our data that will be used for creating the form.
data FileForm = FileForm
{ fileInfo :: FileInfo
, fileDescription :: Text
}
When the form is submitted the development webserver shows this:
-----------------------------11220121616194905281129768369
Content-Disposition: form-data; name="f1"; filename="testfile"
Content-Type: text/x-haskell
...
-----------------------------11220121616194905281129768369
Content-Disposition: form-data; name="f2"
...
Why are these post parameters changed from 'fileInfo' to 'f1', and from 'fileDescription' to 'f2'?
When using lookupPostParam now I need to use "f1" and "f2" as parameter instead of the original field names...
Related
I am having difficulties grabbing files from Dropzone object (using Vue-Dropzone) and appending them to a custom formData object I am building with other params too.
What I am trying to achive is a form with a Dropzone in it which submits via ajax and I am trying to grab all files the user selected and create an object to pass to backend in the form of
files[file_1] = file_1
files[file_2] = file_2
and so on. I have used the below code but no success
let files = {};
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
files[index] = file;
});
// console.log(files);
this.formData.append('files', files);
process_form_via_axios_call
What i get in the backend is:
params={"files"=>"[object Object]"}
While I expect something like this:
{"files" => {"file_1"=>#<ActionDispatch::Http::UploadedFile:0x007fd14c9ec940 #tempfile=#<Tempfile:/var/folders/lk/bhps4r5d3s1fzmywxlp59f480000gn/T/RackMultipart20171002-87936-1tnd839.jpg>, #original_filename="restaurant-person-woman-coffee-medium.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"restaurant-person-woman-coffee-medium.jpg\"\r\nContent-Type: image/jpeg\r\n">......}
How can i achieve this?
Actually was able to resolve this by doing this:
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
this.formData.append('files[file_' + index +']', file);
});
Worked like a charm.
I am doing a CFHTTP post to a web service that is returning two parts (multipart), a XML and PDF. I am looking to get only the PDF. My cfhttp.filecontent is a java.io.ByteArrayOutputStream type. When I do a toString() I get the following
Part 1
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit
Part 2
Content-Type: application/pdf
Content-Transfer-Encoding: binary
I get the response in cfhttp.fileContent and the data looks like the following
--MIME_Boundary
Content-ID: <aa82dfa.N51ec355b.3.15b86044531.59d6>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">....</soapenv:Envelope>
--MIME_Boundary
Content-Id: <2958beaa-dd72-4879-9d80-cc19876b2c2a#example.jaxws.sun.com>
Content-Type: application/pdf
Content-Transfer-Encoding: binary
%PDF-1.4
%ÈÁÄ×
<content removed>
25081
%%EOF
--MIME_Boundary--
I tried to remove all the data that's not related to the PDF but it's still not a valid binary file.
Any thoughts?
From the comments
When I do a cfdump on the fileContent I get the following:
Class Name: java.io.ByteArrayOutputStream
Methods:
close() returns void
reset() returns void
size() returns int
toByteArray() returns byte[]
toString(java.lang.String) returns java.lang.String
toString() returns java.lang.String
toString(int) returns java.lang.String
write(byte[], int, int) returns void
write(int) returns void
writeTo(java.io.OutputStream) returns void
When I invoke toByteArray() I get binary data. I then save the data to a file and I see both XML and PDF parts of the file.
The workaround required two changes: a change to set the accepted encoding value to gzip,deflate and to work with binary data using java.
<cfhttpparam type="HEADER" name="Accept-Encoding" value="gzip,deflate">
Second I needed to manipulate the response using binary methods.
binResponse = result.fileContent.toByteArray();
Next I used a utility from Ben Nadel, Binary.cfc, that has all the binary manipulation I needed. I used the method binarySlice() to extract the start and end part of the binary. The sliced data contains the binary in the exact format that I needed. It was not base64 or any another type, it was binary.
sliced = binNadel.binarySlice( binResponse, <int posistion to start slice>, <int length of binary>));
This solution works, but it's ripe with potential issues, for example the order of the response could switch, the boundary name could change, etc. So this will require a lot of error handling to ensure smooth sailing.
Update:
Next I looked into Leigh's example to see if I could simplify my code. They suggested using Java's MimeMultipart class which supports parsing an MTOM multipart response. Here is the final working code:
<cfscript>
// Modify path as needed
saveToDirec = "c:\temp\";
// Hard coded "boundary" value for DEMO purposes. It MUST match actual value used in cfhttp response
// Best to use cfhttp.responseHeader.content-Type so [if] the service changes your code won't break.
contentType = "multipart/related; boundary=MIME_Boundary;";
// Load and parse ByteArrayOutputStream returned by CFHTTP
dataSource = createObject("java", "javax.mail.util.ByteArrayDataSource").init(m_strSoapResponse.fileContent.toByteArray(), javaCast( "string", contentType));
mimeParts = createObject("java", "javax.mail.internet.MimeMultipart").init(dataSource);
for (i = 0; i < mimeParts.getCount(); i++) {
writeOutput("<br>Processing part["& i &"]");
bp = mimeParts.getBodyPart( javacast("int", i));
// If this part is a PDF, save it to a file.
if (!isNull(bp) && bp.isMimeType("application/pdf")) {
outputFile = createObject("java", "java.io.File").init(saveToDirec &"demo_savedfile_"& i &".pdf");
bp.saveFile(outputFile);
writeOutput("<br>Saved: "& outputFile.getAbsolutePath());
}
}
</cfscript>
Thanks all for your input!
I have to add 3 records in Soap UI request as below:
<soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body>
<wor:AddValues>
<wor:User>
<data:userid>${Properties1#UserID}</data:userid>
</wor:User>
<data:ValuesList>
<data:Value>
<data:Desc>${Properties1#Desc1}</data:Desc>
<data:Details>${Properties1#Detail1}</data:Details>
</data:Value>
<data:Value>
<data:Desc>${Properties1#Desc2}</data:Desc>
<data:Details>${Properties1#Detail2}</data:Details>
</data:Value>
<data:Value>
<data:Desc>${Properties1#Desc3}</data:Desc>
<data:Details>${Properties1#Detail3}</data:Details>
</data:Value>
</data:ValuesList>
</wor:AddValues>
</soapenv:Body>
</soapenv:Envelope>
After completion this will generate unique ID for each value added.
The response would look like below:
<s:Envelop>
<s:Body>
<AddUserValueResult>
<a:ValuesList>
<a:Value>
<a:Id>2501</a:Id>
<a:Desc>Desc1</a:Desc>
<a:Details>Detail1</a:Details>
</a:Value>
<a:Value>
<a:Id>2502</a:Id>
<a:Desc>Desc2</a:Desc>
<a:Details>Detail2</a:Details>
</a:Value>
<a:Value>
<a:Id>2503</a:Id>
<a:Desc>Desc3</a:Desc>
<a:Details>Detail3</a:Details>
</a:Value>
</a:ValuesList>
</AddUserValueResult>
</s:Body>
</s:Envelop>
Using groovy script I want to use the properties Desc1 & Details1,
compare it with response and if it matches then get the ID of that record and store the ID generated in response in another Property Id1.
Continue same for other two records.
Can you please help me to achieve this?
Thanks in Advance!
Here is what to be done one in order to be able to compare the data from request and response:
Create an object model, so that both request and response can be transformed into list of objects, and then compare.
Extract the required data from request / response and build the object list
Need to consider a fact that the both request and response has list of data / values, and it may come in any random order. So, the object should be comparable.
In this case, Script Assertion can be used for comparison without any additional test step. So, add the script assertion to your Soap Request test step.
Here is the script:
/**
* This is script assertion
* Reads both request and response
* Builds the object list, so that both can be compared
**/
//Object Model, req / resp is transformed into this model
#groovy.transform.Canonical
#groovy.transform.Sortable
class Model {
String desc
String details
}
//Closure to retrieve the data element of user choice from xml
def searchData = { data, element ->
def parsedData = new XmlSlurper().parseText(data)
parsedData.'**'.findAll{it.name() == element}
}
//Closure to build the object list of above provided Model class
def buildData = { list, desc, details ->
def objects = []
list.each { item ->
objects << new Model(desc: item."$desc", details: item."$details")
}
objects
}
//Assert if the request and response are non-empty
assert context.rawRequest, "Request is empty or null"
assert context.response, "Response is empty or null"
//Get the Value elements from both request and response
def reqValues = searchData(context.rawRequest, 'Value')
def respValues = searchData(context.response, 'Value')
//Build the Model object list for both request and response and sort them to be able to compare
def reqObjectList = buildData(reqValues, 'Desc', 'Details' ).sort()
def resObjectList = buildData(respValues, 'Desc', 'Details' ).sort()
//Log the above
log.info "Value objects from request: ${reqObjectList.toString()}"
log.info "Value objects from response: ${resObjectList.toString()}"
//Finally, compare the data from both request and response
assert reqObjectList == resObjectList, "Data is not matching"
//Now set the Ids at test case level custom properties
respValues.eachWithIndex { value, index ->
log.info "Id${index+1} : ${value.Id as String}"
context.testCase.setProperty("Id${index+1}", "${value.Id as String}")
}
Now you can use ${#TestCase#Id1} etc.. where ever you need those values.
Note that : it will save those ids if an only if both ValuesList are matched.
You may quickly try it out from Demo till comparing request and response valueslist
I am working with the default Yesod scaffolding project.
I have created a page that displays a simple form to upload files.
(The form will likely be created on the client using Javascript.)
For brevity, the form has a single file input:
<form method="post" action=#{UploadR}>
<input type="file" name="myfile">
<button type="submit">
My objective is to process the form data and then upload the file to a web service.
I have no trouble processing the form, my concern is interacting with the web service.
For example, given the following Yesod handler:
postUploadR :: Handler Html
postUploadR = do
mgr <- fmap httpManager getYesod
fi <- runInputPost $ ireq fileField "myfile"
let fSource = fileSource fi
fName = fileName fi
req <- parseUrl "http://webservice/upload"
let areq = req { method = methodPost
, requestBody = requestBodySourceChunked fSource
}
res <- httpLbs areq mgr
defaultLayout $ do
setTitle "file uploaded"
[whamlet|
<h3> Success
<p> You uploaded #{fName}.
|]
The webservice returns the error: fail post content-length, but everything else works as expected. Perhaps the server doesn't support a chunked request body?
I think your guess about chunked request body is correct. What you need to do is:
Stream the uploaded contents into a temporary file.
Get the size of that file.
Use requestBodySource and provide the file length and its contents.
Fortunately, steps (1) and (2) can be handled quite easily by the sinkCacheLength function. You'll end up with something like:
(fSize, fSource) <- fileSource fi $$ sinkCacheLength
i'm writing webservices for my application.
my problem is that when i call it using GET method it works , but when i use the POST method params doesn't contains ant of my parameters:
when i call using GET , this is the content of params :
params : [username:azerty, test:test, param2:param2, action:editProfile, controller:userWebService]
when i call using POST, this is the content of params :
params : [action:editProfile, controller:userWebService]
EDIT :
/* user controller */
"/service/$action?"(controller: "userWebService", parseRequest: true)
in UserWebServiceController
....
static allowedMethods = [editProfile:['POST', 'GET']]
....
def editProfile(){
println "params : "+ params
....
}
to test i'm using REST console plugin in chrome
params are not sent as a query string in POST requests unlike GET. The parameter strings has to be embedded in the request body in case of POST request.
Use content-type: application/x-www-form-urlencoded
and in the request-body use
username=azerty&test=test
You should be able to see the parameters in params inside controller.
You should be able to see
params : [age:25, name:Hamila, action:editProfile, controller:userWebService]
Did I make you look younger in the test? :)