How to call Post method with multiple parameter from C# project - c#

I Have created a web api project , in values controller i created a method InsertHeading which takes three parameters and returns back a unique id. The method looks like this :-
public int InsertHeading([FromBody]string appid, [FromBody]string type, [FromBody]string detail)
{
int x = 1;
return 1;
}
I tried this variant as well
[HttpPost]
public int InsertHeading(string appid, string type, string detail)
{
int x = 1;
return 1;
}
This piece of code is running when i give url like :- http://server:port/LoggingAPi/Values/InsertHeading
from soap UI.
But when i try to call this method from my c# code i am getting 404 error how ever i try. Here are two ways i have tried :-
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://xxxxx:45422/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var append = new MasterLogInfo() { appid = "2", type = "request", detail = "test call from genesys" };
HttpResponseMessage response = await client.PostAsJsonAsync("LoggingAPI/Values/InsertMasterloginfo", append);
if (response.IsSuccessStatusCode)
{
// Get the URI of the created resource.
Uri finalURL = response.Headers.Location;
}
}
Method 2:-
// client.BaseAddress = new Uri("http://localhost:53117/");
client.BaseAddress = new Uri("http://xxxxxx:45422/");
client.DefaultRequestHeaders.Accept.Clear();
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var user = new MasterLogInfo();
user.appid = "100";
user.typeRequest = "Test";
user.detail = "test call from genesys";
var response = client.PostAsync("LoggingAPI/Values/InsertMasterloginfo", new StringContent(new JavaScriptSerializer().Serialize(user), Encoding.UTF8, "application/json")).Result;
// var response = client.PostAsync("Values/InsertHeading", new StringContent(new JavaScriptSerializer().Serialize(user), Encoding.UTF8, "application/json")).Result;
if (response.IsSuccessStatusCode)
{
// Get the URI of the created resource.
Uri finalURL = response.Headers.Location;
}
}
If i use FromBody tag in parameters i get 500 internal server error, without it i get 404 error. Can anybody tell me what am i missing.I have removed the body of insert Heading for security purpose

Create an object that represents the payload coming in from the post. Then you can use it in the action parameters e.g. public int InsertHeading([FromBody] MyObject myObject)

Related

Resolve Incident in D365 Online via OData

I have created a controller for one of our customers to use. It should run on POST and receive a JSON body with two parameters: id and statuscode.
The logic is simple - I wish to fetch the incident with guid equals id and change its statuscode based on the received value for statuscode.
Code of controller:
public async Task<MyCustomResponse> CloseIncident([FromBody] MyCustomRequest _request)
{
try
{
// Some logic here to check if both Id and StatusCode exist in _request ...
if(Guid.TryParse(_request.Id, out Guid guid))
{
// Construct OData request
JObject incidentResolution = new JObject();
incidentResolution.Add("subject", "testing");
incidentResolution.Add("incidentid#odata.bind", $"/incidents({guid})");
incidentResolution.Add("timespent", 2); //This is billable time in minutes
incidentResolution.Add("description", "description");
JObject parameters = new JObject();
parameters.Add("IncidentResolution", incidentResolution);
if (_request.StatusCode == 1)
{
parameters.Add("Status", (int)IncidentStatusCode.ProblemSolved);
}
else
{
parameters.Add("Status", (int)IncidentStatusCode.SomeOtherRejectedStatusCode);
}
RegenerateAccess(); // Connect to Microsoft Online
string urlAPI = "/api/data/v9.1/CloseIncident";
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(_serviceUrl);
client.Timeout = new TimeSpan(0, 2, 0); //2 minutes
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
//client.DefaultRequestHeaders.Add("Prefer", "return=representation");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpMethod method = HttpMethod.Post;
HttpRequestMessage request = new HttpRequestMessage(method, urlAPI);
request.Content = new StringContent(parameters.ToString(), Encoding.UTF8, "application/json");
// Set the access token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authResult.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return new MyCustomResponse()
{
Status = Status.Success,
Message = "..."
};
}
return new MyCustomResponse()
{
Status = Status.Error,
Message = "..."
};
}
else throw new Exception("Guid is invalid.");
}
catch(Exception ex)
{
return new MyCustomResponse() { Status = Status.Error, Message = ex.Message };
}
}
I'm getting a "Bad Request" from the client.SendAsync line. I think the OData body request is incorrect, but I can't figure out why.
Code looks perfect. I’ll try to add the below header and see the response.
client.DefaultRequestHeaders.Add("Content-Type", "application/json; charset=utf-8");
While trying to debug, if you see any useful error response in exception that will help troubleshooting.

C# HttpClient.SendAsync causes error, cannot send a content-body with this verb-type

I am getting error cannot send a content-body with this verb-type. I am calling a GET Endpoint from a C# VSTO desktop application. What am I doing wrong.
public static string GetCentralPath(LicenseMachineValidateRequestDTO licenseMachine)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Properties.Settings.Default.Properties["JWT"].DefaultValue.ToString());
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri($"{Constants.URL.APIBase}licensemachine/GetCentralPath"),
Content = new StringContent(JsonConvert.SerializeObject(licenseMachine), Encoding.UTF8, "application/json"),
};
using (HttpResponseMessage response = client.SendAsync(request).GetAwaiter().GetResult()) // Causing ERROR
{
var result = GetStringResultFromHttpResponseMessage(response, true);
if (string.IsNullOrEmpty(result))
return null;
return JsonConvert.DeserializeObject<string>(result);
}
}
}
The end point looks like the following:
[HttpGet("GetCentralPath")]
public async Task<IActionResult> GetCentralPath(LicenseMachineValidateRequestDTO dto)
{
// Some code
}
fix the action, you cannot send body data with get, see this post
HTTP GET with request body
[HttpPost("GetCentralPath")]
public async Task<IActionResult> GetCentralPath(LicenseMachineValidateRequestDTO dto)
and fix request , replace Method = HttpMethod.Get with Post, this is what generates an error
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{Constants.URL.APIBase}licensemachine/GetCentralPath"),
Content = new StringContent(JsonConvert.SerializeObject(licenseMachine), Encoding.UTF8, "application/json"),
};

How do you use Basic Authentication with System.Net.Http.HttpClient?

I'm trying to implement a rest client in c# .net core that needs to first do Basic Authentication, then leverage a Bearer token in subsequent requests.
When I try to do Basic Authentication in combination with client.PostAsync with a FormUrlEncodedContent object, I'm getting an exception:
System.InvalidOperationException occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string,string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
//Basic Authentication
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
//make the request
var task = client.PostAsync("/oauth2/token",content);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
at System.Net.Http.Headers.HttpHeaders.GetHeaderDescriptor(String name)
at System.Net.Http.Headers.HttpHeaders.Add(String name, String value)
It looks like you can't use PostAsync and have access to mess with the Headers for authentication. I had to use an HttpRequestMessage and SendAsync.
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/oauth2/token");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
requestMessage.Content = content;
//make the request
var task = client.SendAsync(requestMessage);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
It's not a good practice to create HttpClients explicitly from your calling code.
Please use HttpClientFactory that simplifies a lot of things.
However, if you want to use basic authentication, just create an HttpRequestMessage and add the following header:
var request = new HttpRequestMessage(HttpMethod.Post, getPath)
{
Content = new FormUrlEncodedContent(values)
};
request.Headers.Authorization = new BasicAuthenticationHeaderValue("username", "password");
// other settings
If you decide to use a recommended IHttpClientFactory it's even simpler:
serviceCollection.AddHttpClient(c =>
{
c.BaseAddress = new Uri("your base url");
c.SetBasicAuthentication("username", "password");
})
Don't encode the whole authentication string - encode the "Username:Password" expression and append the result to the "Basic " prefix.
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
content.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);
Also, consider using just ASCII encoding - the UTF8 may not be understood by the server unless you add a charset declaration to the header.
Wikipedia seems to cover this quite well.
The specific problem is this line (below)
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
This fails because HttpContent.Headers (System.Net.Http.Headers.HttpContentHeaders) is only for headers that are content-specific, such as Content-Type, Content-Length, and so on.
You've stated that you can't use DefaultRequestHeaders because you only need it for a single request - but you also can't use it with PostAsync - only SendAsync provided you construct the HttpRequestMessage yourself, as per your own answer and #NeilMoss' answer - but you could use an extension-method in future.
But for the benefit of other readers, another alternative is to add a new extension method based on the existing PostAsync, which is actually really simple (only 3 lines!):
public Task<HttpResponseMessage> PostAsync( this HttpClient httpClient, Uri requestUri, HttpContent content, String basicUserName, String basicPassword, String? challengeCharSet = null, CancellationToken cancellationToken = default )
{
if( basicUserName.IndexOf(':') > -1 ) throw new ArgumentException( message: "RFC 7617 states that usernames cannot contain colons.", paramName: nameof(basicUserName) );
HttpRequestMessage httpRequestMessage = new HttpRequestMessage( HttpMethod.Post, requestUri );
httpRequestMessage.Content = content;
//
Encoding encoding = Encoding.ASCII;
if( challengeCharSet != null )
{
try
{
encoding = Encoding.GetEncoding( challengeCharSet );
}
catch
{
encoding = Encoding.ASCII;
}
}
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(
scheme : "Basic",
parameter: Convert.ToBase64String( encoding.GetBytes( userName + ":" + password ) )
);
return SendAsync( httpRequestMessage, cancellationToken );
}
Usage:
HttpClient httpClient = ...
using( HttpResponseMessage response = await httpClient.PostAsync( uri, content, basicUserName: "AzureDiamond", basicPassword: "hunter2" ).ConfigureAwait(false) )
{
// ...
}
Just something to add that I struggled with, which I only experienced with Basic authentication endpoints. If you add Json as StringContent then it adds a charset=utf-8, this often return a BadRequest 400.
Here is the code I got to fix this: reference:
https://dzone.com/articles/httpclient-how-to-remove-charset-from-content-type
using (var client = new HttpClient())
using (var content = new StringContent(ParseJSON(data), Encoding.Default, "application/json"))
{
//Remove UTF-8 Charset causing BadRequest 400
content.Headers.ContentType.CharSet = "";
var clientId = "client";
var clientSecret = "secret";
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
client.DefaultRequestHeaders.TryAddWithoutValidation(authHeader, authorization);
var response = await client.PostAsync(url, content);
return response;
}
I have resolve this by using below code, that serve my purpose also. Added Code for both Get/Post, this will help you. Moreover I have added one more Header key. So to pass extra data to header. Hope that will resolve your issue.
class Program {
private static readonly string Username = "test";
private static readonly string Password = "test#123";
static void Main(string[] args) {
var response = Login();
}
public static async Task Login()
{
var anotherKey ="test";
HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri("https://google.com/")
};
httpClient.DefaultRequestHeaders.Add($"Authorization", $"Basic {Base64Encode($"{Username}:{Password}")}");
httpClient.DefaultRequestHeaders.Add($"anotherKey", $"{anotherKey}");
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("user/123").ConfigureAwait(false);
// For Get Method
var response= await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
// For Post Method
User user = new User (1,"ABC");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("/post", user).ConfigureAwait(false);
UserDetail userDetail = await httpResponseMessage.Content.ReadAsAsync<UserDetail>().ConfigureAwait(false);
}
}
Using .NET 6, I use the HttpClient.DefaultRequestHeaders.Authorization property to set the Authorization header.
// This example will send a signing request to the RightSignature API
var api = "https://api.rightsignature.com/public/v2/sending_requests";
// requestJson is the serialized JSON request body
var contentData = new StringContent(requestJson, Encoding.UTF8, "application/json");
// Instantiate client (for testing), use Microsoft's guidelines in production
var client = new HttpClient();
// Use basic auth, the token has already been converted to base64
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", tokenB64);
try
{
var response = await client.PostAsync(api, contentData);
}
...
Good luck!

ASP.NET Call to API Endpoint returns 500 - Internal Server Error

I am calling a Rest API using a basic http authentication
public string Get(string LabName)
{
string userName = ConfigurationManager.AppSettings["username"];
string password = ConfigurationManager.AppSettings["password"];
string BaseURL = ConfigurationManager.AppSettings["BaseURL"];
using (var client = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback( delegate { return true; });
Uri uri = new Uri(BaseURL);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var byteArray = Encoding.ASCII.GetBytes(userName+":"+password);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
string clarity_URL = BaseURL + "api/v2/labs?name=" + LabName;
var response = client.GetAsync(clarity_URL).Result;
string responseString = response.Content.ReadAsStringAsync().Result;
return responseString;
}
When I debug the code throws error on the line response like
Can anyone please suggest me what could be the issue.
A 500 Error usually means there is a problem with the API Server.
It would be a good idea to check the specific endpoint for any errors then check again with this code.
If you are checking against a web call that is working correctly, please ensure that the request method (GET / POST / PUT) is correctly aligned and the parameters match.

How to correctly send a PATCH request

I need to call this REST endpoint
PATCH https://graph.windows.net/contoso.onmicrosoft.com/users/username#contoso.onmicrosoft.com?api-version=1.5 HTTP/1.1
{
"<extensionPropertyName>": <value>
}
Please see documentation here: https://msdn.microsoft.com/en-us/library/azure/dn720459.aspx
I have the following code to set the value of one property for a user:
public async Task<ActionResult> AddExtensionPropertyValueToUser()
{
Uri serviceRoot = new Uri(azureAdGraphApiEndPoint);
var token = await GetAppTokenAsync();
string requestUrl = "https://graph.windows.net/mysaasapp.onmicrosoft.com/users/usuario1#mysaasapp.onmicrosoft.com?api-version=1.5";
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent("{ \"extension_33e037a7b1aa42ab96936c22d01ca338_Compania\": \"Empresa1\" }", Encoding.UTF8, "application/json")
};
HttpResponseMessage hrm = await hc.GetAsync(new Uri(requestUrl));
if (hrm.IsSuccessStatusCode)
{
string jsonresult = await hrm.Content.ReadAsStringAsync();
return View("TestRestCall", new SuccessViewModel
{
Name = "The Title",
Message = "The message",
JSON = jsonresult.ToJson()
});
}
else
{
return View();
}
}
However instead of respongint with 204 (No content), its responding with the entire user properties, so I guess something is wrong with my rest CALL
http://screencast.com/t/LmoNswKIf2
I think your problem is this line:
HttpResponseMessage hrm = await hc.GetAsync(new Uri(requestUrl));
This sends an HTTP GET request to the URL that you supply, which in this case references the user "usuario1#mysaasapp.onmicrosoft.com". That is why you are seeing all the properties of the user returned in the response.
I think what you want to do is send the PATCH HttpRequestMessage that you created. To do this you need to use the SendAsync method and supply the HttpRequestMessage as a parameter. If you change the line above to the following, I think you'll set the property value and get your 204 No Content response:
HttpResponseMessage hrm = await hc.SendAsync(request);

Categories