I'm trying to make a Delete action with RestClient but I'm not able to include my Json object inside the request body. This is my code:
var client = new RestClient(WebService);
var request = new RestRequest(string.Format("/api/v1/{0}/{1}", controller, action), method);
request.AddHeader("Accept", ContentTypeApplicationJson);
request.RequestFormat = DataFormat.Json;
request.AddHeader("Content-Type", ContentTypeApplicationJson);
request.AddCookie(".ASPXAUTH", AspxAuth);
request.AddBody(jsonObjectRequest);
var response = client.Execute(request);
var jsonResponse = string.Empty;
using (var stream = new MemoryStream(response.RawBytes))
{
stream.Position = 0;
var sr = new StreamReader(stream);
jsonResponse = sr.ReadToEnd();
}
var jObjectDeserialize = GetJObjectDeserialize(jsonResponse);
In my example ContentTypeApplicationJson = application/json and jsonObject is a object which contain:
{
"BasketItemReferenceGuid": "sample string 1",
"BasketReferenceGuid": "sample string 2"
}
This is my request in Fiddler
DELETE http://local.webapi.com/api/v1/BasketV3Products/ HTTP/1.1
Accept: application/json
Content-Type: application/json
User-Agent: RestSharp 102.4.0.0
Host: local.webapi.com
Cookie: .ASPXAUTH=E6D216034E2CB1A22466A501392B1E2E46601E345B8A0E7743D76CF2270ACFC8ED3C9F1F2F477C4499267222A250E4490291381EE68FE719E094EF1ACDD619B4D792341988F80CB67E8B5037D8ACF9FBABB74DE1E75A530AB432D85722D647771C6C576F8E810257CE9E60117DCEEFBD949EAD9E64C84898BDC5D691F957CE6266CF5652C693B86ED1D55907AAC5DC68
Content-Length: 0
Accept-Encoding: gzip, deflate
Thanks
It looks like even though the HTTP spec doesn't explicitly say that a DELETE should not have a body, many web servers (or web proxies) will ignore it.
Semantically, when using DELETE, the uri should identify the resource to DELETE. This is kind of like using GET. Again, with GET, the spec does not forbid a body, but not many web servers (or web proxies) actually support a body on GET requests.
For more info, see this SO post.
So, the better way to implement your DELETE would be to place BasketItemReferenceGuid and BasketReferenceGuid in the uri template.
Related
I'm trying to do the following cURL call in a c# .net environment
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
The C# code is as follows:
var requestContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
HttpResponseMessage response = await client.PostAsync("http://bms.org/bcknd/republish", requestContent);
HttpContent responseContent = response.Content;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
Console.WriteLine(await reader.ReadToEndAsync());
}
I'm getting a 400 Bad request and when I print it out. Maybe it has something to do with the -XPOST and -d parameter from the curl call?
EDIT:
Here's the http request from curl:
POST http://bms.org/bcknd/republish HTTP/1.1
Host: bms.org/bcknd
User-Agent: curl/7.48.0
Accept: */*
Content-Length: 43
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
Here's the http request from my code:
POST http://bms.org/bcknd/republish HTTP/1.1
Accept: */*
User-Agent: curl/7.48.0
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: bms.org/bcknd
Content-Length: 43
Connection: Keep-Alive
Metadata/Type = "sas"
Short Version
Post the data as StringContent without url encoding and check the response status before trying to read the response body. Make sure the call completes before the application exits, otherwise the call will be cancelled when the application exits. That means, use async Task in Main, not async void :
class Program
{
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new StringContent("Metadata/Type=\"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
if(response.IsSuccessStatusCode)
{
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
else
{
Console.WriteLine($"Oops! {response.StatusCode} - {response.ReasonPhrase}");
}
}
}
Explanation
In cases like this it's very important to know what's actually being sent. To do that, one can use a debugging proxy like Fiddler or Charles.
Curl with -d sends unencoded data. This call :
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
/ and " would have been replaced with other characters if URL encoding was applied. Note also the User-Agent and Accept headers
If --data-urlencode is used, the value will be URL encoded :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 27
Content-Type: application/x-www-form-urlencoded
Metadata/Type =%20%22sas%22
This code on the other hand :
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
Will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Host: www.google.com
Metadata%2FType=%22sas%22
To get the original payload, one can use StringContent with hand-coded content:
var data = new StringContent("Metadata/Type= \"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
The request is :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 19
Host: www.google.com
Metadata/Type= "sas"
If you want to send the User-Agent and Accept headers, you can add them to each individual message or as default request headers :
var client=new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("curl","7.55.1"));
These will add :
Accept: */*
User-Agent: curl/7.55.1
to the request
You can call remote URL as following using HttpClient
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "http://bms.org/bcknd/republish"))
{
request.Content = new StringContent("Metadata/Type = \"sas\"", Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await httpClient.SendAsync(request);
}
}
Here I have just added reference code, by using that you can create your own. I checked your curl request and it seems issue it self.
I'm trying to post to a web form defined as:
<form name="frmdata" method='post' enctype ='multipart/form-data' action ="http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh">
<input type ="hidden" name ="VSS_SERV" value="ZVWSBJXML">
<input type="file" name="filename">
<input type ='submit' name ='x' value ='ODESLI'>
</form>
There is some additional documentation on the form here:
http://www.rzp.cz/docs/RZP02_XML_28.pdf
My latest try:
using (WebClient client = new WebClient())
{
NameValueCollection vals = new NameValueCollection();
vals.Add("VSS_SERV", "ZVWSBJXML");
string filecontent = #"<?xml version=""1.0"" encoding=""ISO-8859-2""?>";
filecontent = filecontent + #"
<VerejnyWebDotaz
elementFormDefault=""qualified""
targetNamespace=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1""
xmlns=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1"" version=""2.8"">";
filecontent = filecontent + #"
<Kriteria>
<IdentifikacniCislo>03358437</IdentifikacniCislo>
<PlatnostZaznamu>0</PlatnostZaznamu></Kriteria>";
filecontent = filecontent + #"</VerejnyWebDotaz>";
vals.Add("filename", filecontent);
client.Headers["ContentType"] = "multipart/form-data";
byte[] responseArray = client.UploadValues(#"http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh", "POST", vals);
string str = Encoding.ASCII.GetString(responseArray);
}
But I can't get past this error:
<KodChyby>-1</KodChyby> (the xml filename does not contain xml defined by namespace)
How can I send this xml data to the form or rather there is a working form - http://stuff.petrovsky.cz/subdom/stuff/RZP/rzp-test-form.php - how to call and catch xml data? I would like to do the same request and get xml.
Using System.Net.Http I was able to construct the form request as a proof of concept using MultipartFormDataContent
Now initially when I tested it, I received 403 Forbidden response but I guessed that was to be expected given my location and that the endpoint might be region locked.
Raw Fiddler response
HTTP/1.1 403 Forbidden
Date: Sat, 27 Oct 2018 01:37:09 GMT
Server: IIS
Content-Length: 225
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /cgi-bin/aps_cacheWEB.sh
on this server.</p>
</body></html>
I was wrong and the forbidden appeared to be the default response for bad requests as you commented that you received the same forbidden error from within the region. So back to the drawing board I went.
I then copied the example HTML form locally and then proceeded to compare the requests from the form (which did actually work) and my code. Gradually making changes to match I was finally able to get a 200 OK response, but the body of the response was empty.
Apparently there was an issue with the server interpreting the boundary in the content type header if it is wrapped in quotes boundary="...".
After more adjustments it then started returning a message based on the content dispositions generated.
HTTP/1.1 200 OK
Date: Sat, 27 Oct 2018 19:55:11 GMT
Server: IIS
Serial: 10.145
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/plain; charset=ISO-8859-2
Content-Length: 169
Multiple definitions of VSS_SERV encountered in input.
If you're trying to do this intentionally (such as with select),
the variable must have a "List" suffix.
So it turns out that the XML API is expecting the request to be in a very specific format. Deviate from that and the request fails.
The MultipartFormDataContent was not generating the request correctly and this caused the server to not behave as expected. Other headers were being placed before the Content-Disposition headers of the parts and the Content-Disposition parameters were also not being enclosed in quotes. So by not including the content-type it in the parts and making sure the content-disposition headers were generated correctly eventually fixed the problem.
It is important to note the order of how the headers are added to the content so that the Content-Disposition header is set first for each part.
Working Code that generates the request in the desired format and gets the XML data.
[Test]
public async Task Post_Form() {
//Arrange
var stream = getXml();
var fileContent = new StreamContent(stream);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
Name = #"""filename""",
FileName = #"""req-details.xml""",
};
fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
var stringContent = new ByteArrayContent(Encoding.UTF8.GetBytes("ZVWSBJXML"));
stringContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
Name = #"""VSS_SERV""",
};
//could have let system generate it but wanteed to rule it out as a problem
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
var form = new MultipartFormDataContent(boundary);
//FIX: boundary quote issue
var contentType = form.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
contentType.Value = contentType.Value.Replace("\"", String.Empty);
form.Add(stringContent);
form.Add(fileContent);
//var data = await form.ReadAsStringAsync(); //FOR TESTING PORPOSES ONLY!!
var client = createHttpClient("http://www.rzp.cz/");
//Act
var response = await client.PostAsync("cgi-bin/aps_cacheWEB.sh", form);
var body = await response.Content.ReadAsStringAsync();
//Assert
response.IsSuccessStatusCode.Should().BeTrue();
body.Should().NotBeEmpty();
var document = XDocument.Parse(body); //should be valid XML
document.Should().NotBeNull();
}
The code above generated the following request, which I extracted using fiddler (Pay close attention to the working format)
POST http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh HTTP/1.1
User-Agent: System.Net.Http.HttpClient
Accept-Language: en-US, en; q=0.9
Accept: text/xml, application/xml
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=---------------------------8d63c301f3e044f
Host: www.rzp.cz
Content-Length: 574
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
-----------------------------8d63c301f3e044f
Content-Disposition: form-data; name="VSS_SERV"
ZVWSBJXML
-----------------------------8d63c301f3e044f
Content-Disposition: form-data; name="filename"; filename="req-details.xml"
Content-Type: text/xml
<?xml version="1.0" encoding="iso-8859-2"?>
<VerejnyWebDotaz xmlns="urn:cz:isvs:rzp:schemas:VerejnaCast:v1" version="2.8">
<Kriteria>
<IdentifikacniCislo>75848899</IdentifikacniCislo>
<PlatnostZaznamu>0</PlatnostZaznamu>
</Kriteria>
</VerejnyWebDotaz>
-----------------------------8d63c301f3e044f--
Which was able to get the following response.
HTTP/1.1 200 OK
Date: Sat, 27 Oct 2018 21:17:50 GMT
Server: IIS
Serial: 10.145
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/xml;charset=ISO-8859-2
Content-Length: 931
<?xml version='1.0' encoding='iso-8859-2'?>
<VerejnyWebOdpoved xmlns="urn:cz:isvs:rzp:schemas:VerejnaCast:v1" version="2.8">
<Datum>27.10.2018</Datum>
<Kriteria>
<IdentifikacniCislo>75848899</IdentifikacniCislo>
<PlatnostZaznamu>0</PlatnostZaznamu>
</Kriteria>
<PodnikatelSeznam>
<PodnikatelID>212fbf8314e01506b0d7</PodnikatelID>
<ObchodniJmenoSeznam Popis="Jméno a příjmení:">Filip Zrůst</ObchodniJmenoSeznam>
<IdentifikacniCisloSeznam Popis="Identifikační číslo osoby:">75848899</IdentifikacniCisloSeznam>
<TypPodnikatele Popis="Typ podnikatele:">Fyzická osoba</TypPodnikatele>
<AdresaPodnikaniSeznam Popis="Adresa sídla:">Vlašská 358/7, 118 00, Praha 1 - Malá Strana</AdresaPodnikaniSeznam>
<RoleSubjektu Popis="Role subjektu:">podnikatel</RoleSubjektu>
<EvidujiciUrad Popis="Úřad příslušný podle §71 odst.2 živnostenského zákona:">Úřad městské části Praha 1</EvidujiciUrad>
</PodnikatelSeznam>
</VerejnyWebOdpoved>
From there it should be small work to parse the resulting XML as needed.
Supporting code
Generate or load the stream of the XML for the form
private static Stream getXml() {
var xml = #"<?xml version=""1.0"" encoding=""ISO-8859-2""?>
<VerejnyWebDotaz
xmlns=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1""
version=""2.8"">
<Kriteria>
<IdentifikacniCislo>75848899</IdentifikacniCislo>
<PlatnostZaznamu>0</PlatnostZaznamu>
</Kriteria>
</VerejnyWebDotaz>";
var doc = XDocument.Parse(xml);//basically to validate XML
var stream = new MemoryStream();
doc.Save(stream);
stream.Position = 0;
return stream;
}
Was gradually able to whittle down the headers needed for a successful request after find the match that worked. Try removing others gradually to test if more can be removed safely to reduce the amount of unnecessary code needed.
private static HttpClient createHttpClient(string baseAddress) {
var handler = createHandler();
var client = new HttpClient(handler);
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "System.Net.Http.HttpClient");
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.9");
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/xml,application/xml");
client.DefaultRequestHeaders.ExpectContinue = false;
client.DefaultRequestHeaders.ConnectionClose = false;
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue() {
MaxAge = TimeSpan.FromSeconds(0)
};
return client;
}
private static HttpClientHandler createHandler() {
var handler = new HttpClientHandler();
// if the framework supports automatic decompression set automatic decompression
if (handler.SupportsAutomaticDecompression) {
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip |
System.Net.DecompressionMethods.Deflate;
}
return handler;
}
While I chose to use the asynchronous API of System.Net.Http, I found a similar question
Reference UploadFile with POST values by WebClient
With an answer that was done using WebClient that could be adapted to your question so that a request can be constructed similar to what was produced above.
I tried testing that one as well but got into the same forbidden error. Now that the correct format is know you should also be able to correctly craft a working request using WebClient/WebRequest
I am creating a restsharp request in order to trigger a batch direct send push request off to Azure notification hub.
I am receiving a 400 Bad Request response, with the message; Could not find 'notifications' part in the multipart content supplied.
The request looks like such;
const string multipartContentType = "multipart/form-data; boundary=\"simple-boundary\"";
const string authSignature = "myvalidauthsignature";
const string url = "mynotificanhuburl";
const string message = "Some message";
var restClient = new RestClient
{
BaseUrl = new Uri(url),
Proxy = new WebProxy("127.0.0.1", 8888),
};
var request = new RestSharp.RestRequest(Method.POST)
{
RequestFormat = DataFormat.Json,
AlwaysMultipartFormData = true
};
request.AddHeader("Content-Type", multipartContentType);
request.AddHeader("Authorization", authSignature);
request.AddHeader("ServiceBusNotification-Format", "gcm");
request.AddParameter("notification", JsonConvert.SerializeObject(new { data = new { message } }), ParameterType.GetOrPost);
request.AddParameter("devices", JsonConvert.SerializeObject(new List<string> { "123", "456" }), ParameterType.GetOrPost);
var response = restClient.Execute(request);
I can see the raw request via Fiddler;
POST https://xxxxx.servicebus.windows.net/xxx/messages/$batch?direct&api-version=2015-04 HTTP/1.1
Authorization: [redacted]
ServiceBusNotification-Format: gcm
Accept: application/json, application/xml, text/json, text/x-json, text/javascript, text/xml
User-Agent: RestSharp/105.2.3.0
Content-Type: multipart/form-data; boundary=-----------------------------28947758029299
Host: [redacted]
Content-Length: 412
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
-------------------------------28947758029299
Content-Disposition: form-data; name="notification"
{"data":{"message":"Some message"}}
-------------------------------28947758029299
Content-Disposition: form-data; name="devices"
["123","456"]
-------------------------------28947758029299--
Which looks about right. If I copy this into postman with the headers etc, I can see the same error response. HOWEVER in postman when I remove the quote marks around the parameter names, it works and returns a 201 Created response.
So this works....
Content-Disposition: form-data; name=notification
This doesn't
Content-Disposition: form-data; name="notification"
Which seems really peculiar. As we are using restsharp however I don't think I have any direct control over the raw output for the request body. I am wondering;
Is there a restsharp setting to manage these quote, perhaps a formatting setting
Why would the Azure endpoint reject a parameter name with quotes
It is possible that the issue is elsewhere and this is a red herring, but this does seem to be responsible.
Appreciate any help...
According our documentation, request should look like this:
POST https://{Namespace}.servicebus.windows.net/{Notification Hub}/messages/$batch?direct&api-version=2015-08 HTTP/1.1
Content-Type: multipart/mixed; boundary="simple-boundary"
Authorization: SharedAccessSignature sr=https%3a%2f%2f{Namespace}.servicebus.windows.net%2f{Notification Hub}%2fmessages%2f%24batch%3fdirect%26api-version%3d2015-08&sig={Signature}&skn=DefaultFullSharedAccessSignature
ServiceBusNotification-Format: gcm
Host: {Namespace}.servicebus.windows.net
Content-Length: 431
Expect: 100-continue
Connection: Keep-Alive
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=notification
{"data":{"message":"Hello via Direct Batch Send!!!"}}
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=devices
["Device Token1","Device Token2","Device Token3"]
--simple-boundary--
So, the name parameter's value is not quoted (name=devices). I've not found any RFC which would explicitly specify requirements regarding the situation. However, in examples inside of RFCs a values appear quoted. And because of that I'm going to fix the service to support both options. Fix should come with next deployment in a week or so.
I was plagued by this for a few days and was diligently searching for a solution with RestSharp and was unable to find one as it always default the content type to "multipart/form-data". I know the OP was looking for a way to do this with RestSharp but I don't believe there is currently.My solution comes from a few different posts over a few days so I apologize for not linking to them. Below is a sample Function to perform a multipart/related POST with json body and base64 pdf string as the file.
public static void PostBase64PdfHttpClient(string recordID, string docName, string pdfB64)
{
string url = $"baseURL";
HttpClient client = new HttpClient();
var myBoundary = "------------ThIs_Is_tHe_bouNdaRY_";
string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"UN:PW"));
client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{url}/api-endpoint");
request.Headers.Date = DateTime.UtcNow;
request.Headers.Add("Accept", "application/json; charset=utf-8");
MultipartContent mpContent = new MultipartContent("related", myBoundary);
mpContent.Headers.TryAddWithoutValidation("Content-Type", $"multipart/related; boundary={myBoundary}");
dynamic jObj = new Newtonsoft.Json.Linq.JObject(); jObj.ID = recordID; jObj.Name = docName;
var jsonSerializeSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
var json = JsonConvert.SerializeObject(jObj, jsonSerializeSettings);
mpContent.Add(new StringContent(json, Encoding.UTF8, "application/json"));
mpContent.Add(new StringContent(pdfB64, Encoding.UTF8, "application/pdf"));
request.Content = mpContent;
HttpResponseMessage response = client.SendAsync(request).Result;
}
I've read every internet post on this and I still don't know what I'm doing wrong. From Fiddler, I'm trying to post multiple items and it's not working. I'm not able to post a .jpeg, but a .txt is no working. First requests to the method are successful, but all subsequent requests aren't and will generate "Unexpected end of MIME multipart stream. MIME multipart message is not complete" or "An asynchronous module or handler completed while an asynchronous operation was still pending". If I just send text entries in the parts/boundaries, it will only see the first one - the others are ignored
Web API
[HttpPost]
[MimeMultipart]
public async Task<IHttpActionResult> PostAsync()
{
var uploadPath = HttpContext.Current.Server.MapPath("~/Uploads");
Directory.CreateDirectory(uploadPath);
var provider = new MultipartFormDataStreamProvider(uploadPath);
// THIS IS WHERE THE ERRORS HAPPEN
await Request.Content.ReadAsMultipartAsync(provider);
// NEVER HAS A NAME(S),, EVEN WHEN NO ERROR
var localFileName = provider.FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();
foreach (var stream in provider.Contents)
{
var fileBytes = stream.ReadAsByteArrayAsync();
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent("Successful upload", Encoding.UTF8, "text/plain");
response.Content.Headers.ContentType = new MediaTypeWithQualityHeaderValue(#"text/html");
}
// omitted code
Fidder Headers
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: localhost:1703
Content-Length: 3199
Fiddler Body
---------------------------acebdf13572468
Content-Disposition: form-data; name="description"
Content-Type: text
the_text_is_here
---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"; filename="today.txt"
Content-Type: text/plain
<#INCLUDE *C:\Users\loser\Desktop\today.txt*#>
---------------------------acebdf13572468-
Should I use a different content-type? I'm dying over here.
All,
I am trying to implement a batch request to Dynamics CRM with the following source code:
public async Task<HttpResponseMessage> HttpPatchCrmApi(string resource, string data)
{
string uniq = Guid.NewGuid().ToString();
MultipartContent content = new MultipartContent("mixed", "batch_" + uniq);
HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, CrmBaseUrl + "/api/data/v8.0/$batch");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, CrmBaseUrl + resource);
request.Content = new StringContent(data, Encoding.UTF8, "application/json");
HttpMessageContent query = new HttpMessageContent(request);
content.Add(query);
batchRequest.Content = content;
HttpResponseMessage response = await RbWebApi.SendAsync(batchRequest);
return response;
}
The problem is that I am getting "400 Bad request"
EDIT:
As suggested in the comments here is the stack trace of the request from fiddler:
POST https://Hidden.api.crm4.dynamics.com/api/data/v8.0/$batch HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV.... very long string
Content-Type: multipart/mixed; boundary="batch_7b6e3c60-1284-4958-a39a-4653af21833c"
Host: Hidden.api.crm4.dynamics.com
Content-Length: 313
Expect: 100-continue
--batch_7b6e3c60-1284-4958-a39a-4653af21833c
Content-Type: application/http; msgtype=request
POST /api/data/v8.0/my_recurringgifts HTTP/1.1
Host: Hidden.api.crm4.dynamics.com
Content-Type: application/json; charset=utf-8
{"my_name":"slavi"}
--batch_7b6e3c60-1284-4958-a39a-4653af21833c--
While writing the code I was inspiring myself from here and here
I think your request is wrong.
You must build the request Body EXACTLY like defined by Microsoft
This means the Blank lines must be there at the right place all the attributes must exist in the body (like "--changeset_XXX" for example) and as I see you dont meet this requirements.
I just build a Request in Postman against my CRM and it worked:
URL
https://yourTenant.api.crm.dynamics.com/api/data/v8.0/$batch
Headers
OData-MaxVersion:4.0
OData-Version:4.0
Accept:application/json
Authorization:Bearer aVeryLongStringTokenHere
Content-Type: multipart/mixed;boundary=batch_1234567
Body
--batch_1234567
Content-Type:multipart/mixed;boundary=changeset_555666
--changeset_555666
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:1
POST https://yourTenant.api.crm.dynamics.com/api/data/v8.0/accounts HTTP/1.1
Content-Type:application/json;type=entry
{name: 'BatchJobTest788'}
--changeset_555666
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:2
POST https://yourTenant.api.crm.dynamics.com/api/data/v8.0/accounts HTTP/1.1
Content-Type:application/json;type=entry
{new_name: 'BatchJobTest348'}
--changeset_555666--
--batch_1234567--
Additional Remarks:
The Content-Type of your Header holds your BatchId
The Content-Type of your Batch holds your ChangesetId (if it is a change to data)
Before starting to programm REST calls try to define them in a REST tool like POSTMAN and make them work. Then build the working request in your code.
Here a good explanation-source for the batching in CRM