RestSharp: Naming the parameter using request.AddJsonBody()? - json

How can I name the piece of multipart/form-data given added to a request when I use the AddJsonBody() method?
I am attempting to get past the obsolete AddParameter() method.
Here's my code using the AddJsonBody() method:
request.AddJsonBody(Metadata);
There's an overload that allows me to specify the Content-Type, but I just need plain old application/json so I'm not using it. Here are the resulting relevant parts of my HTTP request as sent:
POST https://redacted/redacted HTTP/1.1
Content-Type: multipart/form-data; boundary=---------369C5A1F-30CF-450D-A5B4-2DBD93676056
-----------369C5A1F-30CF-450D-A5B4-2DBD93676056
Content-Type: application/json
Content-Disposition: form-data; name="application/json"
{"Date":"2021-07-28T14:27:01.0718841","FurtherInfo":"This is a metadata test."}
-----------369C5A1F-30CF-450D-A5B4-2DBD93676056
Content-Disposition: form-data; name="file"; filename="file20210728T1427010660244Z.txt"
Content-Type: text/plain
CL7~f`lz4ULMJa;]p-q!uH(-z*4iO'SHD)KYER5SI|e{3zW7^}J,%QPyD)$\K"
[...]
-----------369C5A1F-30CF-450D-A5B4-2DBD93676056--
As you can see, the "name" of the added parameter is application/json. I want it to be "metadata" instead. So I'm using this code to get things to be sent how I want, but this code is marked as obsolete:
Parameter metadata = new Parameter("metadata", JsonConvert.SerializeObject(Metadata), "application/json", ParameterType.RequestBody);
request.AddParameter(metadata);
Using this changes the HTTP request to:
POST https://redacted/redacted HTTP/1.1
Content-Type: multipart/form-data; boundary=---------8C24DE69-C111-418A-9C29-5D9DFABA320F
-----------8C24DE69-C111-418A-9C29-5D9DFABA320F
Content-Type: application/json
Content-Disposition: form-data; name="metadata"
{
"date": "2021-07-28T14:45:01.4650889",
"furtherInfo": "This is a metadata test."
}
-----------8C24DE69-C111-418A-9C29-5D9DFABA320F
Content-Disposition: form-data; name="file"; filename="file20210728T1445014611849Z.txt"
Content-Type: text/plain
zoRC)Z:c]\<#/z_q,k
[...]
-----------8C24DE69-C111-418A-9C29-5D9DFABA320F--
The specific serialization doesn't matter, only that it's valid JSON and has the name metadata.
Is there a way to use the newer AddJsonBody() method to do this? Is manipulating the parameter name on the roadmap?

as my understanding you are trying to send a file?
thi sis an example to send a file to API using RestSharp
using (WindowsIdentity.GetCurrent().Impersonate())
{
RestClient client = new RestClient(url);
var request = new RestRequest("/api/Upload", Method.POST, DataFormat.Json);
request.AddHeader("Cache-Control", "no-cache");
request.AlwaysMultipartFormData = true;
request.UseDefaultCredentials = true;
request.AddHeader("Content-Type", "multipart/form-data");
request.AddParameter("ServiceID", ServiceID, ParameterType.QueryString);
request.AddParameter("Description", description, ParameterType.QueryString);
//get byte[] , in reallife would be a stream
byte[] fileData = File.ReadAllBytes($#"C:\1\TEST\{filename}");
request.AddFileBytes("DocumentFile", fileData, filename, "multipart/form-data");
var result = client.Execute(request).Dump();
}

I found it!
From the class definition of RestRequest:
public IRestRequest AddParameter(string name, object value, string contentType, ParameterType type)
Instead of creating the Parameter, I can just add the parameter directly:
request.AddParameter("metadata", JsonConvert.SerializeObject(Metadata), "application/json", ParameterType.RequestBody);
And no more obsolete warning!

Related

Upload file from MultipartEntityBuilder, added headers in file

I upload a JSON file using MultipartEntityBuilder. on another side when I read the file some headers are added on both top and bottom of the file. I tried many things to remove headers from a file but no success.
here is the code how I write multipart request
HttpPost request = new HttpPost("/getFile");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody("file", new File("abc.json"), ContentType.APPLICATION_JSON, "abc.json");
HttpEntity entity = builder.build();
request.setEntity(entity);
uploaded file
--ZD1OkvwbsjLAuhqTLx2I6HzWkZcH9oHoBbTN Content-Disposition: form-data; name="upfile";
filename="abc.json"
Content-Type: application/octet-stream
{ "version": "1.2", }
--ZD1OkvwbsjLAuhqTLx2I6HzWkZcH9oHoBbTN Content-Disposition: form-data; name="text"
This is a multipart post
--ZD1OkvwbsjLAuhqTLx2I6HzWkZcH9oHoBbTN--
how can I send only JSON part not these top and bottom headers

WebAPI: A callback parameter was not provided in the request URI

I am executing a post method in my API using fiddler I get error "A callback parameter was not provided in the request URI.". However, this works for get method.
I have seen several answers to this question, and as per the error I need to specify a callback parameter. However, I'm not sure how to do this using fiddler.
In response to one of those answers from Can I make a jQuery JSONP request without adding the '?callback=' parameter in URL? . I've tried the following in fiddler and I get the same error..
url: http://velopoint-api.localhost.dev/api/v1/tasks?callback=foo
header:
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
ContentType: application/json; charset=utf-8
Authorization: basic "UNQUOTED"
Content-Length: 47
jsonp: true
jsonpCallback: jsonCallback
dataType: jsonp
request body
{ "Title":"New Task", "DueDate":"20-jul-2014" }
Startup
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
// Change Formater to use CamelCasePropertyNamesContractResolver
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
/* Support JsonP */
//register JSONP media type formatter
config.Formatters.Insert(0, new JsonpMediaTypeFormatter(jsonFormatter));
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
...
}
After playing around a little, I've finally figured it wasn't actually routing to the post method.
My header is now
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
Authorization: basic UNQUOTED
Content-Length: 224
Content-Type: application/json
I fixed the problem by specifying Route attribute to my Post method and passing an empty string to the pattern parameter, both on the Get and the Post (as I already have the RoutePrefix attribute specified on the class.
[RoutePrefix("api/v1/tasks")]
[VeloPointAuthorise(perUser: true)]
public class TaskController : BaseApiController
{
[HttpGet]
[Route(template:"", Name = "TaskRoute")]
public HttpResponseMessage Get(int page = 0)
{
....
}
[HttpPost]
[Route(template:"")]
public HttpResponseMessage Post([FromBody] OrganiserTaskModel model)
{
....
}

SmartGWT RestDataSource to Spring REST Controller GET returns 415 Error

This problem has been bugging me all day, and I have spent a lot of time here, and on Google trying to find the right answer and trying lots of different fixes.
I have a Spring MVC Controller which is defined here:
#RequestMapping(value = "/searchAndCount", method = RequestMethod.GET, produces = "application/json", headers =
{ "Accept=application/json", "Content-Type=application/json" }, consumes = "application/json")
public #ResponseBody
RequestResults<?> searchAndCount(#RequestBody SearchInvoiceDTO searchInvoiceDto)
{
RequestResults<?> requestResults = invoiceApprovalService.searchAndCount(searchInvoiceDto);
return requestResults;
}
I know that with most gets, simple parameters can get sent back, but in this case, I found it better to put all my search criteria in one object and send that over. Which is why I am doing a #RequestBody.
Per previous fixes, I made sure that this has both headers that may be needed to accept JSON output.
The JSON String looks like:
String s1 = "{\"userId\":3, \"ddUserId\":301010651, \"customerCode\":\"QA\", \"customerId\":8}";
And yes, I have used the Jackson ObjectMapper tool verify this code will map correctly from this String to an Object, and vice-versa. When I look at the POJO, it does implement Serializable, and it does have a default constructor.
The Junit test works awesome and does return data:
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders.get("/invoices/searchAndCount").contentType(MediaType.APPLICATION_JSON)
.content(test);
this.mockMvc.perform(requestBuilder).andDo(print());
This does make the call to the Controller, I can see from the output what the headers are, and I can see that I actually get back real data which is great. So I feel there is not much more I can do from he controller side.
The real call to the controller comes from the SmartGWT RestDataSource.
The RequestMethod is defined in the datasource, here is the init method:
private InvoiceDataSource(String id)
{
setID(id);
setClientOnly(false);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
fetch.setDataFormat(DSDataFormat.JSON);
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("GET");
fetch.setRequestProperties(fetchProps);
// set up ADD to use POST requests
OperationBinding add = new OperationBinding();
add.setOperationType(DSOperationType.ADD);
add.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest addProps = new DSRequest();
addProps.setHttpMethod("POST");
add.setRequestProperties(addProps);
// set up UPDATE to use PUT
OperationBinding update = new OperationBinding();
update.setOperationType(DSOperationType.UPDATE);
update.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest updateProps = new DSRequest();
updateProps.setHttpMethod("PUT");
// updateProps.setContentType("application/json");
update.setRequestProperties(updateProps);
// set up REMOVE to use DELETE
OperationBinding remove = new OperationBinding();
remove.setOperationType(DSOperationType.REMOVE);
DSRequest removeProps = new DSRequest();
removeProps.setHttpMethod("DELETE");
remove.setRequestProperties(removeProps);
// apply all the operational bindings
setOperationBindings(fetch, add, update, remove);
init();
}
The Fetch is set to POSTMESSAGE which seems to be the best way to pass data using transformReponse.
#Override
protected Object transformRequest(DSRequest dsRequest)
{
// gets the correct URL - (web-app-root)/rest/invoices/searchAndCount
postProcessTransform(dsRequest);
System.out.println("InvoiceDataSource: transformRequest: START");
dsRequest.setContentType("application/json");
JavaScriptObject jso = dsRequest.getData();
// setting more headers, but this doesn't seem to change anything
dsRequest.setAttribute("Access-Control-Allow-Origin", "*");
dsRequest.setAttribute("Content-Type", "application/json");
dsRequest.setAttribute("Accept", "application/json");
String s1 = JSON.encode(jso);
System.out.println("InvoiceDataSource: transformRequest: FINISH: s1=" + s1);
return s1;
}
Since I know I have he correct URL, I also know I am spitting out that the variable "s1" also has the correct JSON data, and again I did test that JSON to make sure it would hit the controller correctly.
I also do have dependencies for Jackson as defined in the pom.xml file. I also have the message converters set in the springmvc-servlet.xml file. If these were not correct, the unit test would not work. However, if you need to see the pom.xml file or the springmvc-servlet.xml file, please let me know.
I have been researching and trying lots of things all day long now, and so far ... no luck.
I hope I have provided enough information, but if you need more, please let me know.
Ultimately, I hope I can tweak my SmartGWT RestDataSource to pass in the correct data to this controller to actually get data out of it.
UPDATE:
When I run this with Jetty in Eclipse, I am using Firefox 23.0.1 to open to my web-app.
Within the Console in Eclipse, here is what I can see:
[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
Host: 127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: GLog=%7B%0D%20%20%20%20left%3A22%2C%20%0D%20%20%20%20top%3A11%2C%20%0D%20%20%20%20width%3A705%2C%20%0D%20%20%20%20height%3A855%2C%20%0D%20%20%20%20priorityDefaults%3A%7B%0D%20%20%20%20%20%20%20%20Log%3A4%0D%20%20%20%20%7D%2C%20%0D%20%20%20%20defaultPriority%3A3%2C%20%0D%20%20%20%20trackRPC%3Atrue%0D%7D
Connection: keep-alive
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1440
Accept: application/json
Notice that the Request header:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
does not show application/json
Also, the Request header: "Content-Type" is not present
When I use Chrome, the result is:
[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
Host: 127.0.0.1:8888
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: max-age=0
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1440
Accept: application/json
When I run from the JUnit test, there is always the "Content-Type:application/json" present. So, it seems that although I tell the SmartGWT RestDataSource that I am using JSON in several places ... the web-services call is not creating the right header.
UPDATE:
I added the following code to the SmartGWT RestDataSource transformRequest method:
Map<String, String> httpHeaders = new HashMap<String, String>();
httpHeaders.put("Accept", "*/*");
httpHeaders.put("Content-Type", "application/json");
dsRequest.setHttpHeaders(httpHeaders);
I can add the "Accept" Request-Header and I still got the 415 Unsupported Media error Message.
When I add the "Content-Type" Request-Header, then I get a 400 BAD REQUEST error message.
I get this in the console now:
[WARN] 400 - GET /rest/invoices/searchAndCount (127.0.0.1) 1418 bytes
Request headers
Host: 127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1418
This took a lot of work, but I finally figured it out.
The verb GET doesn' work for my needs ... I am an idiot. No amount of work is going to make a GET work with sending my parameters as an object in the request data. So let me show the code that I had to change to make this work.
The SmartGWT RestDataSource, I changed;
fetchProps.setHttpMethod("GET"); to fetchProps.setHttpMethod("POST");
an I still kept: fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
fetch.setDataFormat(DSDataFormat.JSON);
// ===========================================
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("POST");
fetchProps.setContentType("application/json");
fetch.setRequestProperties(fetchProps);
Also in the same RestDataSource, in the "transformRequest" method, I added:
Map<String, String> httpHeaders = new HashMap<String, String>();
httpHeaders.put("Content-Type", "application/json");
httpHeaders.put("Accept", "application/json");
dsRequest.setHttpHeaders(httpHeaders);
This made sure that whatever browser I was using, these two headers are being manually set.
The Spring MVC Controller resides in the same web-app, so for now, this avoids any SOP cross-site domain issues until I can test that out. In the meantime, the header for my control looks like:
#RequestMapping(value = "/searchAndCount", method = RequestMethod.POST, headers =
{ "Accept=application/json", "Content-Type=application/json" })
public #ResponseBody
ArrayList<?> searchAndCountPOST(#RequestBody SearchInvoiceDTO searchInvoiceDto)
This work great to get called, and it returned my data. The original unit test I had was doing a MockMVc.get which worked and through me off. It must have recognized data coming through and changed the GET to a POST to make it work. My unit test was changed to a POST now, and that also works well.
I hope all this work pays off for someone else!

WCF Make a POST request with a parameters

I have a wcf service that exposes a rest endpoint. I want to test it using fiddler. I have a method like this :
[WebInvoke(Method = "POST", UriTemplate = "EditContact", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string EditContact(string idContact, Contact Contact);
I input :
POST http://192.168.1.31/ContactLibrary2.0/Service.svc/rest/DeleteContact HTTP/1.1
User-Agent: Fiddler
Host: 192.168.1.31
Content-Type : application/json; Charset=UTF-8
{
"idContact":"67697",
"firstName":"6767",
"lastName":"afdgsg",
"email":"dfghdfdb",
"age":"120",
"street":"sdf",
"city":"dfghgfhjhdfgsdv",
"country":"sdfsd"
}
More code from my project you can see : HERE
I get http 400 error (bad request error). Ideas ?
Your request should look as shown below:
POST http://192.168.1.31/ContactLibrary2.0/Service.svc/rest/DeleteContact HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
{
"idContact":5,
"Contact":{
"idContact":"67697",
"firstName":"6767",
"lastName":"afdgsg",
"email":"dfghdfdb",
"age":"120",
"street":"sdf",
"city":"dfghgfhjhdfgsdv",
"country":"sdfsd"
}
}

Can't set the content-type to Json in functional test

I have the following code to make a json request, but it fails:
Response response = POST("/b/profile/","application/json",body);
I also tried this one:
Response response = POST("/b/profile/","application/x-www-form-urlencoded",body);
but the response again had the content-type text/html. Any ideas?
Try
Response response = POST("/b/profile/","text/javascript",body);
See here for more details