Passing a custom object to a REST endpoint with C# - c#

I have a rest endpoint that accepts a single custom object parameter containing two properties.
Let's call the param InfoParam
public class InfoParam
{
public long LongVar { get; set; }
public string StringVar { get; set; }
}
My code I have is as follows:
infoParam.LongVar = 12345678;
infoParam.StringVar = "abc"
var myRequest = (HttpWebRequest)WebRequest.Create(url);
myRequest.Method = "POST";
var content = string.Empty;
using (var theResponse = (HttpWebResponse)MyRequest.GetResponse())
{
using (var stream = theResponse.GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
content = sr.ReadToEnd();
}
}
}
So I have the InfoParam variable, with the two values, but I can't figure out where to pass it in to the REST endpoint.

You need to turn the object into a stream of bytes that can be added to the Request stream - which will in turn be sent as the HTTP POST body. The format of these bytes needs to match what the server expects. REST endpoints usually expect these bytes to resemble JSON.
// assuming you have added Newtonsoft.JSON package and added the correct using statements
using (StreamWriter writer = new StreamWriter(myRequest.GetRequestStream()) {
string json = JsonConvert.SerializeObject(infoParam);
writer.WriteLine(json);
writer.Flush();
}
You'll probably want to set various other request parameters, like the Content-Type header.

You have to write it int the `Content (and set content-type). Check out How to: Send data by using the WebRequest class
The recommendation is to use System.Net.Http.HttpClient instead.
Please note that you should know what content the server expects ('application/x-www-form-urlencoded`, json, etc.)
The following snippet is from POST JSON data over HTTP
// Construct the HttpClient and Uri. This endpoint is for test purposes only.
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://www.contoso.com/post");
// Construct the JSON to post.
HttpStringContent content = new HttpStringContent(
"{ \"firstName\": \"Eliot\" }",
UnicodeEncoding.Utf8,
"application/json");
// Post the JSON and wait for a response.
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
uri,
content);
// Make sure the post succeeded, and write out the response.
httpResponseMessage.EnsureSuccessStatusCode();
var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
Debug.WriteLine(httpResponseBody);
In your case the content would be something like this
HttpStringContent content = new HttpStringContent(
JsonConvert.SerializeObject(infoParam), // using Json.Net;
UnicodeEncoding.Utf8,
"application/json");

Related

C# REST API File Upload from Client Code gets 400 Bad Request

I have a Rest API written in .Net core that accepts a File as input as Multipart/Form-data. The API works absolutely fine when I run it from Swagger/Postman.
Here is the API endpoint.
[HttpPost("CreateStudy")]
public ActionResult CreateStudy([FromForm] APIRequest request)
{
// rest of the code
Also here is the APIRequest object. it has only one property which is IFormFile Type.
public class APIRequest
{
public IFormFile XMLFile { get; set; }
}
So far it works well. The problem is that I am trying to write a client side code that will call this API and pass the File from C# code.
But I am always getting a 400-Bad request in the client code.
This is the client code I am trying with.
public string CallServiceWithFileAsync(string EndPointURL, string FilePath)
{
string ResultStatusCode;
Uri uri = new Uri(EndPointURL);
var Client = new HttpClient();
Client.DefaultRequestHeaders.Clear();
//Prepare Message
HttpRequestMessage Message = new HttpRequestMessage();
Message.Method = HttpMethod.Post;
Message.Headers.Add("Accept", "application/octet-stream");
Message.RequestUri = new Uri(EndPointURL);
using (Stream fileStream = File.OpenRead(FilePath))
{
var content = new StreamContent(fileStream);
var response = Client.PostAsync(uri, content);
ResultStatusCode = response.Result.StatusCode.ToString();
}
return ResultStatusCode;
}
What am I doing wrong here? What is the correct way of sending a file into REST endpoint ?
[FromForm] expects an accept header with application/x-www-url-formencoded. If this is not the case, check your output-logs to see why the request is not processed.

Custom header in HttpClient POST request not working

I am trying to send a POST request when using HttpClient. When I run the code I am getting an unauthorized response. But I am able to get it to work in PostMan. Below is my current code snippet and pictures of what I am trying to perform. I'd like to add I am trying to send a json string in my body.
using (HttpClient client = new HttpClient())
{
var connectionUrl = "https://api.accusoft.com/prizmdoc/ViewingSession";
var content = new Dictionary<string, string> { { "type", "upload" }, { "displayName", "testdoc" } };
// Serialize our concrete class into a JSON String
var stringPayload = JsonConvert.SerializeObject(content);
// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
//client.DefaultRequestHeaders.Add("Acs-Api-Key", "aPsmKCmvkZHf9VakCmfHB8COmzRxXY5FDhj8F1FU1IGmQlOkfjiKESKxfm38lhey");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Acs-Api-Key", "aPsmKCmvkZHf9VakCmfHB8COmzRxXY5FDhj8F1FU1IGmQlOkfjiKESKxfm38lhey");
// Do the actual request and await the response
var httpResponse = httpClient.PostAsync(connectionUrl, httpContent).Result;
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
// Do something with response. Example get content:
var connectionContent = httpResponse.Content.ReadAsStringAsync().Result;
}
else
{
// Handle a bad response
return;
}
}
}
You're using two HttpClients when you only need to use one.
using (HttpClient client = new HttpClient())
and
using (var httpClient = new HttpClient())
The second one (httpClient) is doing the post but the authentication header has been added to client. Just remove the second one (httpClient) and make sure you use client.PostAsync(...) to send the request.
I'd also consider using await, rather than .Result (see why here) when sending the request:
var httpResponse = await client.PostAsync(connectionUrl, httpContent);
In addition to haldo's answer,
In your code, you are adding your Acs-Api-Key header as and Authorization header, meaning it ends up looking like Authorization: Acs-Api-Key (key) rather than Acs-Api-Key: (key) which is what you have in PostMan.
Instead of adding it as an Authorization header, just add it as a regular header.
client.DefaultRequestHeaders.Add("Acs-Api-Key","(key)");
Also something else that may cause issues is that you aren't wrapping your content in the "source" object like you are in PostMan. There are a couple ways of doing this
The first would be to simply wrap it in it's string format:
stringPayload = $"\"source\":{{{stringPayload}}}"
Or you can do it before you serialize by making your own object instead of having a Dictionary
var content = new PayloadObject(new Source("upload", "testdoc"));
var stringPayload = JsonConvert.SerializeObject(content);
// Send the request
class PayloadObject{
Source source {get; set;}
PayloadObject(Source source){
this.source = source;
}
}
class Source{
string type {get; set;}
string displayName {get; set;}
Source(string type, string displayName){
this.type = type;
this.displayName = displayName;
}
}

How to make GET with request body

How do I make a GET query with request body using RestSharp (I'm using RestSharp v. 106.6.10 with .NET Core 2.2). I can do it using WebClient/Postman etc. no problem.
Here's the code failing with {["A non-empty request body is required."]}.
var client = new RestClient("BaseUri");
var request = new RestRequest("URL", Method.GET);
request.AddJsonBody(Body);
client.Execute(request); // {["A non-empty request body is required."]}
This represents a valid use case, pity if it's not supported.
Update: The motivation for having GET requests with body is to avail of get requests having complex parameters, which can't be nicely encoded into a query string. I know people serialize their jsons an put them into a querystrings but I'd rather put it into a request body, considering it's a permissible usage after all.
From my experience AddJsonBody is completely broken (had a multiple times when it wasn't serialize my model just pasting in Body something like MyProject.Models.MyModel or even left body empty). So I use following:
var client = new RestClient("BaseUri");
var request = new RestRequest("URL", Method.GET);
request.AddHeader("Content-Type", "application/json");
string serializedBody = Newtonsoft.Json.JsonConvert.SerializeObject(Body);
request.AddParameter("application/json; charset=utf-8", serializedBody, ParameterType.RequestBody);
client.Execute(request);
UPDATE sorry i wasn't patient when reading you question. Answer is provided for RestSharp not PostSharp
I solved it by using reflection, to trick WebRequest that it is legal to send a body with a GET request.
1.Create Model for body parameters,
public class SampleBodyModel
{
public String name{ get; set; }
public String password{ get; set; }
}
Initialize the request.
SampleBodyModel sampleRequest = new SampleBodyModel
{
name= "name",
password= "password"
};
//initialize the API call
var request = WebRequest.Create("API_END_POINT");
request.ContentType = "application/json";
request.Method = "GET";
var myHttpWebRequest = (HttpWebRequest)request;
// ------- Add these two lines if your using JWT token -------
myHttpWebRequest.PreAuthenticate = true;
myHttpWebRequest.Headers.Add("Authorization", "Bearer " + "TOKEN");
// -----------------------------------------------------------
var type = myHttpWebRequest.GetType();
var currentMethod = type.GetProperty("CurrentMethod",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(myHttpWebRequest);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(currentMethod, false);
//Add the Request body
using (var streamWriter = new
StreamWriter(myHttpWebRequest.GetRequestStream()))
{
streamWriter.Write(Newtonsoft.Json.JsonConvert.SerializeObject(sampleRequest));
}
var response = (HttpWebResponse)myHttpWebRequest.GetResponse();
var responseStream = response.GetResponseStream();
var myStreamReader = new StreamReader(responseStream, Encoding.Default);
var json = myStreamReader.ReadToEnd();
responseStream.Close();
response.Close();

Why I can't correctly call this API passing an object into the request body?

I am pretty new in .NET and C# (I came from Java and Spring framework) and I am finding the following difficulties calling an API in the correct way.
I will try to explain my problem in details.
I have this API (defined into a project deployed into IIS. Note, this project contains also other APIs that I am calling without problem):
[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo(MailBuffer mailBuffer, string siglaIDUor)
{
..........................................................................
DO SOMETHING
..........................................................................
}
As you can see it take 2 input parameters:
MailBuffer mailBuffer that should be into the request body.
siglaIDUor that is into the URI.
I have some problem trying to pass the first parameter.
NOTE: I can't change the code of this API because was made by someone else and it can have impact on other thing.
Into another project deployed elsewhere I am trying to call the previous API (from a controller method) in this way:
[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public IHttpActionResult InviaMailAlProtocollo(string siglaIdUor)
{
Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);
// Ignore self signed certificate of the called API:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// Create the byte array[]
UTF8Encoding encoding = new UTF8Encoding();
byte[] mailContent = encoding.GetBytes("TEST");
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
string jsonRequest = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/ABC123";
// Setting my credentials:
credCache.Add(new Uri(jsonRequest), "NTLM", myCreds);
HttpWebRequest spRequest = (HttpWebRequest)HttpWebRequest.Create(jsonRequest);
spRequest.Credentials = credCache;
spRequest.UserAgent = "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0";
spRequest.Method = "POST";
spRequest.Accept = "application/json;odata=verbose";
spRequest.ContentType = "application/json; charset=UTF-8";
// Create and set the stream:
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
newStream.Close();
// Obtain the response from the API:
HttpWebResponse endpointResponse = (HttpWebResponse)spRequest.GetResponse();
string sResult;
JArray jarray;
// Parse the response:
using (StreamReader sr = new StreamReader(endpointResponse.GetResponseStream()))
{
sResult = sr.ReadToEnd();
jarray = JArray.Parse(sResult);
//JObject jobj = JObject.Parse(sResult);
}
Console.WriteLine(jarray);
return Ok(jarray);
}
The problem is that when this method call my API the received MailBuffer mailBuffer input parameter is null (I see it debuggin my API and calling it).
I suspect that the problem could be related to this code section of my call:
// Create and set the stream:
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
newStream.Close();
Probably I am trying to attach the wrong thing into the body of my request (the byte[] mailContent instead of the entire MailBuffer content object).
NOTE: To perform this call I have to use HttpWebRequest.
So, what is wrong? What am I missing? How can I fix this issue putting the entire MailBuffer content object into the body request and allowing my called API to retrieve it as input parameter?
The other project should make sure that the request is made with properly formatted data expected by the other API.
Right now you are sending just the raw bytes of the test email in the body of the request
//...
// Create the byte array[]
UTF8Encoding encoding = new UTF8Encoding();
byte[] mailContent = encoding.GetBytes("TEST");
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
//...
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length); //<---HERE ONLY SENDING encoding.GetBytes("TEST")
while the other endpoint is expecting data that can be deserialized to MailBuffer
Here is the portion of code that should be refactored to send the correct data
//...
UTF8Encoding encoding = new UTF8Encoding();
// Create the MailBuffer object that have to be passed to the API into the request body:
var content = new MailBuffer() {
Nome = "blablabla",
Buffer = encoding.GetBytes("TEST")
};
//convert model to JSON using Json.Net
var jsonPayload = JsonConvert.SerializeObject(content);
byte[] mailContent = encoding.GetBytes(jsonPayload); //<---CORRECT CONTENT NOW
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
//...
Finally I would suggest using the simpler API of HttpClient to make the request. That however would be entirely up to your preference.
Here is an example of same call using HttpClient
[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public async Task<IHttpActionResult> InviaMailAlProtocollo(string siglaIdUor) {
Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);
// Ignore self signed certificate of the called API:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
string requestUri = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/" + siglaIdUor;
// Setting my credentials:
credCache.Add(new Uri(requestUri), "NTLM", myCreds);
var handler = new HttpClientHandler {
UseDefaultCredentials = true,
Credentials = credCache
};
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("UserAgent", "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json;odata=verbose"));
// Create the MailBuffer object that have to be passed to the API into the request body:
var buffer = new MailBuffer() {
Nome = "blablabla",
Buffer = Encoding.UTF8.GetBytes("TEST")
};
//convert model to JSON using Json.Net
var jsonPayload = JsonConvert.SerializeObject(buffer);
var mailContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
// Obtain the response from the API:
var response = await client.PostAsync(requestUri, mailContent);
if (response.IsSuccessStatusCode) {
var jarray = await response.Content.ReadAsAsync<JArray>();
Console.WriteLine(jarray);
return Ok(jArray);
}
return BadRequest();
}
Use [FromBody] parameter.
[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo([FromBody]MailBuffer mailBuffer, string siglaIDUor)
{
..........................................................................
DO SOMETHING
..........................................................................
}
Also try passing MailBuffer as JSON object, it will be automaticly converted to MailBuffer object when you pass this from body.
If this won't work switch MailBuffer object in method with similar object and then map this object to MailBuffer.
You can give a try with HttpClient (using System.Net.Http)
private static readonly HttpClient client = new HttpClient();
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
var values = new Dictionary<string, object>
{
{ "mailBuffer", content },
{ "siglaIDUor", siglaIdUor }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("/api/XXX/InviaAlProtocollo/ABC123", content);
var responseString = await response.Content.ReadAsStringAsync();

How to post json into HTTP server

I was wondering how can I make a post from a json to an http server.
The code I'm using to do json is as follows:
pedro product = new pedro();
product.FirtsName = "Ola";
product.ID = 1;
product.idade= 10;
string json = JsonConvert.SerializeObject(product);
And this is the pedro class:
public class pedro
{
public int ID { get; set; }
public string FirtsName { get; set; }
public int idade { get; set; }
}
With WebApi, you can use something like this:
string url = "http://url.of.server/";
Pedro product = new Pedro();
product.FirtsName = "Ola";
product.ID = 1;
product.Idade = 10;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PostAsJsonAsync(url, product).Result;
if (response.IsSuccessStatusCode)
{
// do something
}
}
If you're not using WepApi there are many similar methods, for instance: https://stackoverflow.com/a/39414248/7489072
Don't Base64 encode the body of your post, as suggested in the comments, unless you absolutely must / want to post binary files AND have control over the receiving webserver. Webservers in 99% of the cases expect a plain text body.
If you need to post characters outside the ASCII range, use the correct HTTP headers to specify a Unicode body load.
Update 1 (headers):
The HttpClient class has property DefaultRequestHeaders that can be used to set common request headers, such as AcceptEncoding. If you need a more fine grained control of the content headers, use .PostAsync(string uri, HttpContent content) in stead of .PostAsJsonAsync (that just sets some default headers for Json content)
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string stringFromObject = JsonConvert.SerializeObject(product);
HttpContent content = new StringContent(stringFromObject, Encoding.UTF8, "application/json");
content.Headers.Add("YourCustomHeader", "YourParameter");
HttpResponseMessage response = client.PostAsync(url, content).Result;
if (response.IsSuccessStatusCode)
{
// do something
}
}
Update 2 (encoding):
To elaborate more on the encoding comments: of course you should escape quotes and the likes. But this is part of the Json standard and should be handled by common encoders / decoders. On top of that, you can use any further encoding for the properties of your serialized object. For instance HTML-encoding on strings and Base64 on binary properties. As long as you know the webserver receiving it will decode it properly.
{
"id": 3,
"title": "Decode this",
"description": "this is < HTML encoded >",
"profileImgBase64": "Nzg5MzQ4IHdleWhmQVMmKihFJiphc3R5WUdkdCphc14qVHlpZg0K"
}
So encode individual properties, but don't encode the whole Json payload, as you would have to decode it at the beginning of the receiving pipeline and it's just not something webservers will understand.

Categories

Resources