Upload file to Solr with HttpClient and MultipartEntity - file-upload

httpclient, httpmime 4.1.3
I am trying to upload a file through http to a remote server with no success.
Here's my code:
HttpPost method;
method = new HttpPost(solrUrl + "/extract");
method.getParams().setParameter("literal.id", fileId);
method.getParams().setBooleanParameter("commit", true);
MultipartEntity me = new MultipartEntity();
me.addPart("myfile", new InputStreamBody(doubleInput, contentType, fileId));
method.setEntity(me);
//method.setHeader("Content-Type", "multipart/form-data");
HttpClient httpClient = new DefaultHttpClient();
HttpResponse hr = httpClient.execute(method);
The server is Solr.
This is to replace a working bash script that calls curl like this,
curl http://localhost:8080/solr/update/extract?literal.id=bububu&commit=true -F myfile=#bububu.doc
If I try to set "Content-Type" "multipart/form-data", the receiving part says that there's no boundary (which is true):
HTTP Status 500 - the request was rejected because no multipart boundary was found
If I omit this header setting, the server issues an error description that, as far as I discovered, indicates that the content type was not multipart [2]:
HTTP Status 400. The request sent by the client was syntactically incorrect ([doc=null] missing required field: id).
This is related to [1] but I couldn't determine the answer from it. I was wondering,
I am in the same situation but didn't understand what to do. I was hoping that the MultipartEntity would tell the HttpPost object that it is multipart, form data and have some boundary, and I wouldnt set content type by myself. I didn't quite get how to provide boundaries to the entities - the MultipartEntity doesn't have a method like setBoundary. Or, how to get that randomly generated boundary to specify it in addHeader by myself - no getBoundary methor either...
[1] Problem with setting header "Content-Type" in uploading file with HttpClient4
[2] http://lucene.472066.n3.nabble.com/Updating-the-index-with-a-csv-file-td490013.html

I am suspicious of
method.getParams().setParameter("literal.id", fileId);
method.getParams().setBooleanParameter("commit", true);
In the first line, is fileId a string or file pointer (or something else)? I hope it is a string. As for the second line, you can rather set a normal parameter.
I am trying to tackle the HTTP Status 400. I dont know much Java (or is that .Net?)
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error

Related

How to POST a XML file in jmeter body instead of a physical file

I'm using JMeter 3.2.
My requirement is to read an XML file from the disk, replace some tags with dynamic values to ensure each thread sends a unique xml file upload (NOT SOAP Request). The following code in JSR223 sampler works perfectly fine when I try to upload the newfile through POST using a http sampler with ${newfilename} file text/xml.
import org.apache.commons.io.FileUtils;
try {
String content = FileUtils.readFileToString(new File("E:/test.xml"));
content = content.replaceAll("SUB_ID", "${__UUID}");
content = content.replaceAll("ABN_ID", "${empabn}");
content = content.replaceAll("EMPNAME", "${empname}");
vars.put("content", content);
FileUtils.writeStringToFile(new File("E:/testnew${empname}.xml"), content);
}
catch (Throwable ex) {
log.info("What happened?", ex);
throw ex;
}
Instead of writing again to the disk and uploading again, how can I send the contents of string 'content' as part of request body? I have looked at many posts that talk about the input output streams but they are confusing. When I try to send just ${content} in body, the application throws following error:
HTTP Status 500 - Could not write JSON: Name is null (through reference chain: com.xxx.xxx.datafile.rest.DataFileResponse["validationStatus"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Name is null (through reference chain:
Appreciate your help.
Multipart POST requests which are being used for files upload are different from normal POST requests hence there no possibility to simply substitute the file with the generated in-memory string.
You need to replicate the request exactly as it would be send by JMeter or real browser and manually populate each part starting from defining boundary using the HTTP Header Manager and ending up with the creation of Content-Disposition and specify your file contents there.
A little hint: you don't need to generate/substitute values for each call, it is enough to replace them once and JMeter will substitute them on its own given you use __eval() and __FileToString() functions combination.
You can check out Testing REST API File Uploads in JMeter for an example of creation a relatively complex file upload request, in your case it will be easier but still tricky.

Append URL Query Strings to request

I'm trying to send a POST request and format the query string in a specific format. Order doesn't matter aside from the first parameter, but I haven't been successful.
What I need:
localhost/someapp/api/dosomething/5335?save=false&userid=66462
What some of my attempts have spit out:
http://localhost/someapp/api/dosomething/?Id=29455&save=false&userId=797979
http://localhost/someapp/api/dosomething/?save=false&userId=797979
How I formatted the request:
request.AddQueryParameter("Id", "29455");
request.AddQueryParameter("save", "false");
request.AddQueryParameter("user", "4563533245");
If I try AddParameterfor Id it doesn't get appended on the query string (I'm thinking because it's a POST and not a GET), so that won't work. The API isn't expecting a form, it's expecting :
(string id, List<Dictionary<string,string>>)
I could use a StringBuilder, but that feels wrong. I'm not sure if UrlSegment is the best way to go either, since I would basically be hacking the query string. Is there a way to format my request in the format I need using RestSharp's API?
What I ended up using is UrlSegment and then kept the .AddQueryParameter methods, so the final code block looks like :
var url = new RestClient(localhost/someapp/api/dosomething/{id});
var request = new RestRequest(Method.POST);
request.AddParameter("Id", "5335", ParameterType.UrlSegment);
request.AddQueryParameter("save", "true");
request.AddQueryParameter("UserId", "5355234");
Which produced the URI I needed.
The easiest coding process for using RestSharp or any other API client library would be to use Postman to generate if you are unsure of how to code it. Download Postman, do a new request, enter the URL string to send to the API, click on Code, select C# (RestSharp) from the dropdown. Here is the code it generated.
var client = new RestClient("http://localhost/someapp/api/dosomething /5335?save=false&userid=66462");
var request = new RestRequest(Method.POST);
request.AddHeader("Postman-Token", "bd05aa45-f1b9-4665-a3e7-888ad16f2800");
request.AddHeader("cache-control", "no-cache");
IRestResponse response = client.Execute(request);

restsharp accept-encoding disabling compression

In a particular case I need to be able to disable compression in the requst/response.
Using Firefox RestClient I am able to post some xml to a web service and get some response xml successfully with a single header parameter "Accept-Encoding" : " "
which if I do not set this header, the response body would come back compressed with some binary data in the response body(that's why I want to disable gzip in response)
Now using the same header value in my app (using RestSharp in C#), I still get the binary data (gzip) in response.
Can someone please shed some light? Is it supported in RestSharp?
RestSharp does not support disabling compression.
If you look at the source code in Http.Sync.cs line 267 (assuming a sync request, async has the same code duplicated in Http.Async.cs line 424)
webRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None;
that is, the underlying WebRequest that Restsharp uses to make the http call has the compression options hardcoded. There is an open issue that documents this
The feature (only just) seems to have been added, but stealthily - without a note on the issue's status nor on the changelogs. Possibly as it hasn't been sufficiently tested?
Nevertheless I recently had a need for this functionality and tested it - and it works. Just set the RestClient instance's AutomaticDecompression property to false.
If you intend to keep your RestClient instance long-lived remember to do this before its first use - the setting seems to be 'locked in' after use and cannot change after. In my case I needed to make calls with and without AutomaticDecompression so i simply created two different RestClient instances.
Using RestSharp v106.11.4, I was unable to turn off automatic decompression as Bo Ngoh suggested. I set the AutomaticDecompression on the RestClient instance at the moment it gets instantiated, but still the Accept-Encoding header was added.
The way to set this & disable the decompression is through the ConfigureWebRequest method, which is exposed on the RestClient. Below snippet allowed me to turn off this feature:
var client = new RestClient();
client.ConfigureWebRequest(wr =>
{
wr.AutomaticDecompression = DecompressionMethods.None;
});
Not sure if this relevant anymore, but for maybe future references
RestRequest has IList<DecompressionMethods> AllowedDecompressionMethods, and when creating new RestRequest the list is empty. Only when calling the Execute method it fills with the default values (None, Deflate, and GZip) unless it's not empty
To update the wanted decompression method, simply use the method named AddDecompressionMethod and add the wanted decompression method - and that's that
Example:
var client = new RestClient();
var request = new RestRequest(URL, Method.GET, DataFormat.None);
request.AddDecompressionMethod(DecompressionMethods.GZip);
var response = client.Execute(request);
As of RestSharp version 107, the AddDecompressionMethod has been removed and most of the client options has been move to RestClientOptions. Posting here the solution that worked for me, in case anyone needs it.
var options = new RestClientOptions(url)
{
AutomaticDecompression = DecompressionMethods.None
};
_client = new RestClient(options);

ASP.NET Web API - Reading querystring/formdata before each request

For reasons outlined here I need to review a set values from they querystring or formdata before each request (so I can perform some authentication). The keys are the same each time and should be present in each request, however they will be located in the querystring for GET requests, and in the formdata for POST and others
As this is for authentication purposes, this needs to run before the request; At the moment I am using a MessageHandler.
I can work out whether I should be reading the querystring or formdata based on the method, and when it's a GET I can read the querystring OK using Request.GetQueryNameValuePairs(); however the problem is reading the formdata when it's a POST.
I can get the formdata using Request.Content.ReadAsFormDataAsync(), however formdata can only be read once, and when I read it here it is no longer available for the request (i.e. my controller actions get null models)
What is the most appropriate way to consistently and non-intrusively read querystring and/or formdata from a request before it gets to the request logic?
Regarding your question of which place would be better, in this case i believe the AuthorizationFilters to be better than a message handler, but either way i see that the problem is related to reading the body multiple times.
After doing "Request.Content.ReadAsFormDataAsync()" in your message handler, Can you try doing the following?
Stream requestBufferedStream = Request.Content.ReadAsStreamAsync().Result;
requestBufferedStream.Position = 0; //resetting to 0 as ReadAsFormDataAsync might have read the entire stream and position would be at the end of the stream causing no bytes to be read during parameter binding and you are seeing null values.
note: The ability of a request's content to be read single time only or multiple times depends on the host's buffer policy. By default, the host's buffer policy is set as always Buffered. In this case, you will be able to reset the position back to 0. However, if you explicitly make the policy to be Streamed, then you cannot reset back to 0.
What about using ActionFilterAtrributes?
this code worked well for me
public HttpResponseMessage AddEditCheck(Check check)
{
var request= ((System.Web.HttpContextWrapper)Request.Properties.ToList<KeyValuePair<string, object>>().First().Value).Request;
var i = request.Form["txtCheckDate"];
return Request.CreateResponse(HttpStatusCode.Ok);
}

400 error message when trying to create a new document in CouchDb using XMLHttpRequest

I am trying to create a new document in a couchDB database but with the following code I get a '400 bad request' response. I want to create a document that does not contain any other information than the _id (and of course the generated _rev).
var xhrCreate = new XMLHttpRequest();
xhrCreate.open('PUT','http://domainName:5984/dbName/docName/', true);
xhrCreate.setRequestHeader("Content-type", "application/json");
xhrCreate.send();
The CouchDb documentation says that a 400 error indicates a:
"Bad request structure. The error can indicate an error with the request URL, path or headers. Differences in the supplied MD5 hash and content also trigger this error, as this may indicate message corruption."
Could anyone point me in the right direction? Hints and help is much appreciated.
CouchDB requires an empty document as part of the content:
xhrCreate.send('{}');