Trouble with HTTP post in Android - c#

I am fairly new at android development. Here is my problem:
I have this endpoint: http://bdzservice.apphb.com/api/Image which accepts POST requests
The body of the request is a String, example:
/SearchServlet?action=showMap&id1=25&date=09/12/2013&st1=5216000&st2=5229030
Invalid example: {"/SearchServlet?action=showMap&id1=25&date=09/12/2013&st1=5216000&st2=5229030"}
Invalid example2: {mapHref : "/SearchServlet?action=showMap&id1=25&date=09/12/2013&st1=5216000&st2=5229030"}
Invalid example3: {"mapHref" : "/SearchServlet?action=showMap&id1=25&date=09/12/2013&st1=5216000&st2=5229030"}
this is the code I've written so far:
HttpClient client = new DefaultHttpClient();
String message;
HttpPost httpPost = new HttpPost("http://bdzservice.apphb.com/api/Image");
try
{
message = url;
StringEntity se = new StringEntity(message, "UTF8");
se.setContentType("application/json");
httpPost.setEntity(se);
httpPost.setHeader("Content-type", "application/json");
HttpResponse resp = client.execute(httpPost);
if (resp != null)
{
if (resp.getStatusLine().getStatusCode() == 204)
result = true;
}
Log.d("Status line", "" + resp.getStatusLine().getStatusCode());
}
I always get an error when trying to post data, but when I manually (through a REST client) post data I get an OK result.
Can someone help me with this?
EDIT
This is the endpoint, It is written in C# (Web Api)
EDIT 2: Tried modifying the service to return body it recieved (see the comment in the url) and it retruns null, so the problem is it is not getting the body (or just reading it wrong)

I have created a library here for .NET Standard that does POST and PUT. I have tested it thoroughly on Android. There are quick start samples to get going quickly. The sample only has a PUT, but the principle should be the same:
https://bitbucket.org/MelbourneDeveloper/restclient-.net/overview

Related

C# HttpClient cannot send authorization header to ASP.NET Core Web API

I am trying to use C# HttpClient from ASP.NET MVC to make a request to an API. My API is running on .NET 6.0.
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{token}");
var serialized = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync(urlToSend, serialized);
Here is my code. I tried all the possibilities I saw on google. But when sending request, I can't send Authorization header.
I can send it with Postman.
Here is my API code:
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(IResult))]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword([FromBody] ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return GetResponseOnlyResult(await Mediator.Send(changePasswordCommand));
}
Note: In my _url, I use http, not https.
I'm not sure but maybe the [AllowAnonymous]attribute remove the Authorization header from request just because it does not make sense if no authorization is needed.
Have you checked if the sent request contains the header using a tool like fiddler ?
I solved the problem by changing my base url from HTTP to HTTPS.
I tried with Fiddler and I got the same problem when I request to HTTP.
So thanks to #olivier-duhart .
To add to the accepted answer, the problem gets solved by changing from HTTP to HTTPS is due to the fact that, the Authorization header gets stripped during redirects.
This behavior is for security concerns and is by design, as mentioned in the github discussion here.
The same behavior may not be seen when using Postman vs HttpClient for example, is due to the way that different clients, have differing mechanisms, by which the subsequent requests (following a response status 30X) to the redirect location are handled.
Also a great answer elsewhere on stackoverflow : Authorization header is lost on redirect
Please review this link. Allow Anonymous will ignore the authentication header
https://github.com/dotnet/aspnetcore/issues/30546
I tried with the code. It seems working fine for me. Here is my code of console app
try
{
ChangePasswordCommand passobj = new ChangePasswordCommand() { password = "new password"};
string _url = "https://localhost:44395/api/Values/";
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"MYTOKEN");
var serialized = new StringContent(JsonConvert.SerializeObject(passobj), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync("changeuserpassword", serialized);
}
catch (Exception ex) {
}
And here is controler Api
[AllowAnonymous]
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword(ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return Ok();
}

ASP.net MVC clients consume patch API get Bad request 400 returns

I'm using asp.net MVC 5 to consume API that also developed in asp.net MVC.
For POST and GET requests, I managed to make it work, except for PATCH that always get 400 bad request from web service.
This is what I do in my client controller:
using (HttpClient httpClient1 = new HttpClient())
{
string apiURLGetClientApproval = "/clients/approvals?action=" + actionType;
HttpMethod method = new HttpMethod("PATCH");
HttpRequestMessage message = new HttpRequestMessage(method, new Uri(baseAddress + apiURLGetClientApproval));
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token.AccessToken);
message.Content = content;
var result = httpClient1.SendAsync(message).Result;
}
This is the content that I pass from my client to API:
{{"clients": [
{
"cn": "1132196",
"hitdate": "04/05/2021"
}]}}
PS :
I access API by postman and ajax from client side with this content, got success reponse.
I have tried with these solution, but same 400 error bad request responsed : PATCH Async requests with Windows.Web.Http.HttpClient class
This is how the parameter of API look like:
[CustomAuth(Roles = "Super Admin, Admin, User")]
[HttpPatch]
[Route("clients/approvals")]
public HttpResponseMessage UpdateClientApproval(HttpRequestMessage request, string action, [FromBody]JObject data)
{..... }
I have been dealing with the same exact problem for 2 days now. I just fixed it. I realised that sending a PATCH request probably required some specific payload [{"op":"replace"....}] as we can tell from using PostMan. However the PUT request doesn't, in fact most of the data on the business object would already be populated, so you modify what you want to change and send a PUT request instead. I just did that. I had to add the PUT action method in my controller and change the HttpClient to send a PUT request and it worked less than 5mins ago.

RestSharp http statut code and response statut of 0 "Error: SecureChannelFailure (The authentication or decryption has failed.)"

I'm trying to query data on my mlab mongodb database with their rest api but I can't get it to work.
To makehttp rest requests in my Xamarin application I'm using restSharp, I tried many times but I always get an empty answer, not event the header so I really don't know where the problem is coming from.
Here is my code:
class HttpDataHandler
{
static String stream = null;
public String GetHttpData(String collection)
{
var uri = "https://api.mlab.com";
var client = new RestClient(uri);
var request = new RestRequest("api/1/databases/{db}/collections/{coll}" , Method.GET);
request.AddParameter("apiKey", Common.API_KEY); // adds to POST or URL querystring based on Method
request.AddUrlSegment("db", Common.DB_NAME);
request.AddUrlSegment("coll", collection);
IRestResponse response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
System.Console.WriteLine(response.Content); // raw content as string
else
System.Console.WriteLine(response.StatusDescription + " " + response.ResponseUri+" "+ response.StatusCode);
return stream;
}
}
mLab is using TLS1.2. Changing the SSL/TLS implementation of your Android project from Default to Native TLS 1.2+ most likely will fix the issue.
See the HttpClient Stack and SSL/TLS documentation for an extensive explanation on these 2 combo-boxes that are provided in Xamarin Android projects.

Differences between using C# HttpClient API and the postman testing? Client call works on postman, but not C# httpClient getAsync

I am testing a REST API post, and it works well when I try it on Postman. However, in some scenario (related to the posting XML data) if I post with HttpClient API, I would receive the following error:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
But the same XML content works fine on Postman with status OK and proper response.
What is the differences between using the C# HttpClient API and the postman testing? How can I configure my API call to match with the behavior on postman?
Here I attached the source code, and the Postman screenshot
public void createLoan()
{
string baseCreateLoanUrl = #"https://serverhost/create?key=";
var strUCDExport = XDocument.Load(#"C:\CreateLoan_testcase.xml");
using (var client = new HttpClient())
{
var content = new StringContent(strUCDExport.ToString(), Encoding.UTF8, Mediatype);
string createLoanApi = string.Concat(baseCreateLoanUrl, APIKey);
try
{
var response = client.PostAsync(createLoanApi, content).Result;
}
catch (Exception ex)
{
MessageBox.Show("Error Happened here...");
throw;
}
if (response.IsSuccessStatusCode)
{
// Access variables from the returned JSON object
string responseString = response.Content.ReadAsStringAsync().Result;
JObject jObj = JObject.Parse(responseString);
if (jObj.SelectToken("failure") == null)
{
// First get the authToken
string LoanID = jObj["loanStatus"]["id"].ToString();
MessageBox.Show("Loan ID: " + LoanID);
}
else
{
string getTokenErrorMsg = string.Empty;
JArray errorOjbs = (JArray) jObj["failure"]["errors"];
foreach (var errorObj in errorOjbs)
{
getTokenErrorMsg += errorObj["message"].ToString() + Environment.NewLine;
}
getTokenErrorMsg.Dump();
}
}
}
Thanks for Nard's comment, after comparing the header, I found the issue my client header has this:
Expect: 100-continue
While postman doesn't has.
Once I removed this by using the ServicePointManager:
ServicePointManager.Expect100Continue = false;
Everything seems fine now. Thanks all the input!
My gut tells me it's something simple. First, we know the API works, so I'm thinking it's down to how you are using the HttpClient.
First things first, try as suggested by this SO answer, creating it as a singleton and drop the using statement altogether since the consensus is that HttpClient doesn't need to be disposed:
private static readonly HttpClient HttpClient = new HttpClient();
I would think it would be either there or an issue with your content encoding line that is causing issues with the API. Is there something you are missing that it doesn't like, I bet there is a difference in the requests in Postman vs here. Maybe try sending it as JSON ala:
var json = JsonConvert.SerializeObject(strUCDExport.ToString());
var content = new StringContent(json, Encoding.UTF8, Mediatype);
Maybe the header from Postman vs yours will show something missing, I think the real answer will be there. Have fiddler running in the background, send it via Postman, check it, then run your code and recheck. Pay close attention to all the attribute tags on the header from Postman, the API works so something is missing. Fiddler will tell you.
I was struggling with this for 2 days when I stumbled over Fiddler which lets you record the traffic to the service. After comparing the calls I saw that I had missed a header in my code.

How to get response data from RestSharp to download for user?

I am using RestSharp to call an HTTP service via a Querystring. The service generates a Word document.
When I call this service, it looks like a Word document is being returned in the "Content" property, but I struggling to work out how to return this content to the user via the traditional download window as a word document for saving.
public ActionResult DocGen(string strReportId)
{
var client = new RestClient("http://localhost:88");
var request = new RestRequest("DocGen/{id}", Method.GET);
request.AddUrlSegment("id", "1060"); // replaces matching token in request.Resource
// execute the request
//RestResponse response = (RestResponse) client.Execute(request);
IRestResponse response = client.Execute(request);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
var myException = new ApplicationException(message, response.ErrorException);
throw myException;
}
// Important and simple line. response.rawbytes was what I was missing.
return File(response.RawBytes,response.ContentType,"sample.doc");
}
Should this be an action?
The content type seems correct ie Word.11
So how do I code get this Response.Content back to the user?
Many thanks in advance.
EDIT
I was closer to the solution than I thought. Power to RestSharp I guess !! See above. Now there might be a better way, and I am all ears for any suggestions, but this is where I am at at present.
return File(response.RawBytes,response.ContentType,"sample.doc");
In case anyone may benefit.

Categories

Resources