.net core 3 status code 405 in response when using httpClient and Fiddler? - asp.net-core-mvc

Why am I getting status code 405 in response when using httpClient or Fiddler?
I am getting a status code 405 response when tyring to access a net core 3.1 wepapi action method that accepts json string sent in the body as shown below.
The status code 405 occurs when the request is sent in a net core 3.1 console app using httpClient.
In Fiddler the request works fine.
The webapi action code is
[RequireHttps]
[HttpPut("setkdatainformation/{id:int:min(0):max(5)}/{info}")]
[Authorize(Roles = "Admin")]
public async Task<string> SetKDataInformation(int id, string info, [FromBody] string kinfo)
{
The request is sent from a .net core 3.1 console app using http client as shown below
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(newMediaTypeWithQualityHeaderValue($"application/json"));
var dData = $"{q}kdata test{q}";
var content = new StringContent(dData, System.Text.Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync(${url}api/v1.0/KDataServer/setkdatainformation/{connectionId}/{headerLoginName}", content);

The status code 405 occurs when the request is sent in a net core 3.1 console app using httpClient.
I did a test based on the code snippet you shared, which work well on my side. If possible, you can try to create a new project and create controller with only this action method then test if it can work well.
var q = "\"";
var connectionId = 2;
var headerLoginName = "test";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue($"application/json"));
var dData = $"{q}kdata test{q}";
var content = new StringContent(dData, System.Text.Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync($"https://xxxx/setkdatainformation/{connectionId}/{headerLoginName}", content);
In Fiddler the request works fine
You mentioned the request could be processed fine if you make it via fiddler, to troubleshoot the issue, you can capture the request that you sent from console app using fiddler, then compare the request url, header(s) and body etc with that working one you sent through fiddler and make sure you are making request(s) to same endpoint from both fiddler and console app.

Related

client.DeleteAsync - include object in body

I have an ASP.NET MVC 5 website - in C# client code I am using HttpClient.PutAsJsonAsync(path, myObject) fine to call a Json API (the API is also mine created in Web API).
client.BaseAddress = new Uri("http://mydomain");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PutAsJsonAsync("api/something", myObj);
I would like to do the same with a Delete verb. However client.DeleteAsync does not allow an object to be passed in the body. (I would like to record the reason for deletion alongside the Id of the item to delete in the URI).
Is there a way to do this?
You'll have to give up a little in terms of convenience since the higher-level DeleteAsync doesn't support a body, but it's still pretty straightforward to do it the "long way":
var request = new HttpRequestMessage {
Method = HttpMethod.Delete,
RequestUri = new Uri("http://mydomain/api/something"),
Content = new StringContent(JsonConvert.SerializeObject(myObj), Encoding.UTF8, "application/json")
};
var response = await client.SendAsync(request);

Get "API key is missing" error when querying account details to Mailchimp API 3.0 using RestSharp

When using RestSharp to query account details in your MailChimp account I get a "401: unauthorized" with "API key is missing", even though it clearly isn't!
We're using the same method to create our RestClient with several different methods, and in all requests it is working flawlessly. However, when we're trying to request the account details, meaning the RestRequest URI is empty, we get this weird error and message.
Examples:
private static RestClient CreateApi3Client(string apikey)
{
var client = new RestClient("https://us2.api.mailchimp.com/3.0");
client.Authenticator = new HttpBasicAuthenticator(null, apiKey);
return client;
}
public void TestCases() {
var client = CreateApi3Client(_account.MailChimpApiKey);
var req1 = new RestRequest($"lists/{_account.MailChimpList}/webhooks", Method.GET);
var res1 = client.Execute(req1); // works perfectly
var req2 = new RestRequest($"automations/{account.MailChimpTriggerEmail}/emails", Method.GET);
var res2 = client.Execute(req2); // no problem
var req3 = new RestRequest(Method.GET);
var res3 = client.Execute(req3); // will give 401, api key missing
var req4 = new RestRequest(string.Empty, Method.GET);
var res4 = client.Execute(req4); // same here, 401
}
When trying the api call in Postman all is well. https://us2.api.mailchimp.com/3.0, GET with basic auth gives me all the account information and when debugging in c# all looks identical.
I'm trying to decide whether to point blame to a bug in either RestSharp or MailChimp API. Has anyone had a similar problem?
After several hours we finally found what was causing this..
When RestSharp is making the request to https://us2.api.mailchimp.com/3.0/ it's opting to omit the trailing '/'
(even if you specifically add this in the RestRequest, like: new RestRequest("/", Method.GET))
so the request was made to https://us2.api.mailchimp.com/3.0
This caused a serverside redirect to 'https://us2.api.mailchimp.com/3.0/' (with the trailing '/') and for some reason this redirect scrubbed away the authentication header.
So we tried making a
new RestRequest("/", Method.GET)
with some parameters (req.AddParameter("fields", "email")) to make it not scrub the trailing '/', but this to was failing.
The only way we were able to "fool" RestSharp was to write it a bit less sexy like:
new RestRequest("/?fields=email", Method.GET)

ASP.NET authorization using RestSharp

On the site "example.net" I have standart api method to take access token (which i can call /Token) and if I make POST request using Fiddler to example.net/Token with parameters in request body
And all is OK. Status code 200 and in the response access token and other info.
But if I do this request from other site using RestSharp - 500 Internal Server Error. I tried to AddParameter, AddBody, AddObject. Make parameters as a JSON string, to change DataFormat, to AddHeader of Content-Type. This is my last version of request.
request = new RestRequest(URL, Method.POST);
//request.AddHeader("Content-Type", ContentType);
string UrlEncoded = "";
//I parse Parameters to format like I use in request body in Fiddler.
if (Parameters.Count != 0)
foreach (var param in Parameters)
UrlEncoded = param.ParamToUrlEncoded(UrlEncoded);
request.AddBody(UrlEncoded);
IRestResponse response = client.Execute(request);
var content = response.Content;
Do i need to set any more attributes on the request or something?
Thank you.

calling webapi from asp.net application as a client

I have a web api with a method to return string value. I am having this web api controller inside a web application. I am trying to call the method in the api using the following code:
Stream data = client.OpenRead(new Uri("http://localhost:40786/api/Getvalues/getstring"));
StreamReader reader = new StreamReader(data);
string s = reader.ReadToEnd();
Console.WriteLine(s);
data.Close();
reader.Close();
I am calling this method on click event of a web-page say Default.aspx. The code runs fine however instead of calling web-api and returning it's value, it is returning HTML mark-up of the page on which i have the button. No idea what is happening. Can anyone suggest what I am missing here ?
Try this code:
WebClient client = new WebClient();
string resultStr = client.DownloadString(http://localhost:40786/api/Getvalues/getstring");

Do Get request with a complex type parameter in the request body with web api

I want to do an integration test for the below action.
How can I pass my requestDto object in the integration test?
Neither the GetAsync nor SendAsync method has an overload parameter to pass a custom object to the server.
[Route("{startDate:datetime}")]
[HttpGet]
public HttpResponseMessage Get(DateTime startDate, [FromBody]LessonplannerGetRequest request)
{
request.StartDate = startDate;
var lessonplannerResponse = _service.GetPeriodsByWeekStartDate(request);
return Request.CreateResponse<LessonplannerResponse>(HttpStatusCode.OK, lessonplannerResponse);
}
[Test]
public void Get_Lessons_By_Date()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, _server.BaseAddress + "/api/lessonplanner/2014-01-14");
var myRequestDto = new LessonplannerGetRequest();
// Act => QUESTION: HOW do I pass the myRequestDto ???
var response = _client.SendAsync(request, new CancellationToken()).Result;
// Assert
Assert.That(response.StatusCode == HttpStatusCode.OK);
}
UPDATE
As Darrel Miller said:"Technically HTTP says you can send a body, it just says the body doesn't mean anything and cannot be used. HttpClient won't let you send one."
I post here my integration test with HttpClient doing a Get request with complex type + FromBody:
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, _server.BaseAddress + "/api/lessonplanner/2014-01-14");
var myRequestDto = new LessonplannerGetRequest{ FirstDayOfWeek = DayOfWeek.Sunday, SchoolyearId = 1, StartDate = DateTime.Today};
request.Content = new ObjectContent<LessonplannerGetRequest>(myRequestDto, new JsonMediaTypeFormatter());
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Act
var response = _client.SendAsync(request, new CancellationToken()).Result;
// Assert
Assert.That(response.StatusCode == HttpStatusCode.OK);
Of course is this is not the Http way some might consider doing it differentlly sending complex type via FromUri/query string.
HTML specifications says you cannot send a GET with a body.
HTTP specs allows it.
WebAPI allows it, because it is a service/REST and implements HTTP but not HTML, but many clients and browser won't allow it because they implement both specs and try to be strict.
As for the specifications (RFC1866, page 46; HTML 4.x section 17.13.3) itself, it states:
If the method is "get" and the action is an HTTP URI, the user agent takes the value of action, appends a `?' to it, then appends the form data set, encoded using the "application/x-www-form-urlencoded" content type.
(e.g. if you do a <form> with GET, it will parse all the form params and set them in the query string ?a=b).
In term of pure HTTP and in the context of REST services, nothing prevents that behavior, but not all clients will be able to handle it. It's mostly a best-practice advise when it comes to REST/WebAPI to not handle body data from HttpGet, only URI data (the opposite, POST /action?filter=all is usually tolerated for metadata/action qualifiers, but that's another discussion).
So yeah, it's at your own risk, even if used only internally. As not all clients handle it (e.g. HttpRequestMessage), so you might run into trouble like you have.
You should NOT pass a GET body with HTTPClient.

Resources