Cant get the asp.net Web API token using http client - c#

I am trying to use http Client to make a call to Web API to get the token.I have one MVC app and Web API app.below is the MVC controller action I have.
[HttpPost]
public ActionResult Login()
{
LoginModel m = new LoginModel();
m.grant_type = "password";
m.username = "xxx";
m.password = "xxx1234";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:51540/");
var response = client.PostAsJsonAsync("Token", m).Result;
response.EnsureSuccessStatusCode();
return View();
}
But when I make the request the API responds as BAD request. I tried to add the content type as "application/json" and have confirmed using fiddler that the request is of type json.
I am able to register the user using Web API so at WebAPI side things are looking fine to me,I am using default project created by VS2013 using Individual account and haven't modified any thing on API side.
I am following this tutorial http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api and trying to use HTTP Client instead of fiddler.
I will be thankful if someone helps me

TokenEndpointRequest seems doesn't support JSON yet, but you can use query string
var response = client.PostAsync("Token", new StringContent("grant_type=password&username=xxx&password=xxx1234", Encoding.UTF8)).Result;

Here's my code from the answer & comment above
using (var client = new HttpClient{ BaseAddress = new Uri(BaseAddress) })
{
var token = client.PostAsync("Token",
new FormUrlEncodedContent(new []
{
new KeyValuePair<string,string>("grant_type","password"),
new KeyValuePair<string,string>("username",user.UserName),
new KeyValuePair<string,string>("password","P#ssW#rd")
})).Result.Content.ReadAsAsync<AuthenticationToken>().Result;
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(token.token_type, token.access_token);
// actual requests from your api follow here . . .
}
created an AuthenticationToken class for beautification purposes:
public class AuthenticationToken
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
}

Related

C# .NET Core 3.1 Web API Post parameter is Null

I am trying to make a post request from WPF to Web API using the following code but the request parameter is always null.
Request Model
public class Document
{
public string FileName { get; set; }
public byte[] Buffer { get; set; }
}
public class Request
{
public string Uploader { get; set; }
public List<Document> Documents { get; set; }
}
WPF Client
var obj = new Request()
{
Uploader = "John Doe",
Documents = new List<Document>
{
new Document()
{
FileName ="I Love Coding.pdf",
Buffer = System.IO.File.ReadAllBytes(#"C:\Users\john.doe\Downloads\I Love Coding.pdf.pdf")
}
}
};
using (var http = new HttpClient())
{
var encodedJson = JsonConvert.SerializeObject(obj);
var conent = new StringContent(encodedJson, Encoding.UTF8, "application/json");
HttpResponseMessage response = await http.PostAsync("https://my-app.com/api/upload", conent);
response.EnsureSuccessStatusCode();
}
Web API
[Route("")]
public class AppController : ControllerBase
{
[HttpPost]
[Route("api/upload")]
public async Task<IActionResult> UploadDocumentsAsync([FromBody] Request request)
{
// request is always null when app is running in production
// https://my-app.com/api/upload
//request is not null when running on https://localhost:8080/api/upload
}
}
Please what am I missing in the above implementation?
The request parameter is not null on localhost but always null in production.
Please what am I missing in the above implementation? The request
parameter is not null on localhost but always null in production.
Well, not sure how are getting data on local server becuse, you are sending MultipartFormData means your POCO object and file buffer. As you may know we can send json object in FromBody but not the files as json. Thus, I am not sure how it working in local and getting null data is logical in IIS Or Azure.
what am I missing in the above implementation?
As explained above, for sending both POCO object and Files as byte or steam we need to use FromForm and beside that, we need to bind our request object as MultipartFormDataContent to resolve your null data on your UploadDocumentsAsync API action.
Required Change For Solution:
WPF:
In your WPF http request please update your request code snippet as following:
var obj = new Request()
{
Uploader = "John Doe",
Documents = new List<Document>
{
new Document()
{
FileName ="I Love Coding.pdf",
Buffer = System.IO.File.ReadAllBytes(#"YourFilePath")
}
}
};
var httpClient = new HttpClient
{
BaseAddress = new("https://YourServerURL")
};
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(obj.Uploader), "Uploader");
formContent.Add(new StringContent(obj.Documents[0].FileName), "Documents[0].FileName");
formContent.Add(new StreamContent(new MemoryStream(obj.Documents[0].Buffer)), "Documents[0].Buffer", obj.Documents[0].FileName);
var response = await httpClient.PostAsync("/api/upload", formContent);
if (response.IsSuccessStatusCode)
{
var responseFromAzureIIS = await response.Content.ReadAsStringAsync();
}
Note: Class in WPF side would remain same as before. No changes required.
Asp.net Core Web API:
In asp.net core web API side you should use [FromForm] instead of [FromBody]
So your controller Action would as following:
[Route("")]
public class AppController : ControllerBase
{
[HttpPost]
[Route("api/upload")]
public async Task<IActionResult> UploadDocumentsAsync([FromForm] Request file)
{
if (file.Documents[0].Buffer == null)
{
return Ok("Null File");
}
return Ok("File Received");
}
}
Note: For remote debugging I have checked the logs and for double check I have used a simple conditionals whether file.Documents[0].Buffer == null. I have tested both in local, IIS and Azure and working accordingly.
Update POCO Class in API Project:
For buffer you have used byte for your WPF project but for Web API project update that to IFormFile instead of byte. It should be as following:
public class Document
{
public string FileName { get; set; }
public IFormFile Buffer { get; set; }
}
public class Request
{
public string Uploader { get; set; }
public List<Document> Documents { get; set; }
}
Output:
If you would like to know more details on it you could check our official document here

How to add below request body (string) in Postman using http client?

Postman Post call Screenshot
Hi Below is my current code:
var url = "https://localhost:44332/token";
var login = new Login()
{
username = "test#gmail.com",
password = "Password#1",
grant_type = "password"
};
using (var client = new HttpClient())
{
httpResponseMessage = await client.PostAsJsonAsync(url, login);
if (httpResponseMessage.IsSuccessStatusCode)
{
var token = httpResponseMessage.Content.ReadAsStringAsync();
}
}
My error is that 400: Bad Request, whenever i make the API call.
If i use postman, its working,
The following is what i put in POSTMAN body:
"username=test#gmail.com&password=Password#1&grant_type=password"
Many Thanks in advance if anyone can correct me!
It looks like you're trying to get hte token from OAuth 2.0 authentications server. You shouldn't be posting JSON - it expects the data as form. It returns a JSON object with access token storen in property access_token - you probably will need to deserialize it as well.
using System.Net.Http.Json;
using System.Text.Json.Serialization;
var url = "https://localhost:44332/token";
var form = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username","test#gmail.com#1"},
{"password", "Password#1"},
};
using (var client = new HttpClient())
{
var response = await client.PostAsync(url, new FormUrlEncodedContent(form));
if (response.IsSuccessStatusCode)
{
var token = await response.Content.ReadFromJsonAsync<Token>();
var accessToken = token.AccessToken;
}
}
class Token
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("token_type")]
public string TokenType { get; set; }
[JsonPropertyName("expires_in")]
public int ExpiresIn { get; set; }
[JsonPropertyName("refresh_token")]
public string RefreshToken { get; set; }
}
Do you pass these parameters by URL in postman? This form username=test#gmail.com&password=Password#1&grant_type=password looks like you use URL past parameters in postman.
Usually, in POST requests we pass parameters in the request body, not the URL.
Besides, a recommendation is not directly a HttpClient instance. If you use .NET Framework and create the HttpClient instance directly, cannot release the socket resource even if you disposable the HttpClient object. If you use .NET Core, you can inject an HttpClient or IHttpClientFactory.
Refers: Use IHttpClientFactory to implement resilient HTTP requests

SSL/TSL issue when attempting to post message from C# to Slack

I am attempting to post a message using web hooks through Slack in ASP.NET MVC C#. I am getting a SSL/TLS issue when attempting to execute. My code looks great, and I've compared it to several tutorials out there without finding any differences. Here is my SlackClient.cs :
public class SlackClient
{
private readonly Uri _uri;
private readonly Encoding _encoding = new UTF8Encoding();
public SlackClient(string urlWithAccessToken)
{
_uri = new Uri(urlWithAccessToken);
}
//Post a message using simple strings
public void PostMessage(string text, string username = null, string channel = null)
{
Payload payload = new Payload()
{
Channel = channel,
Username = username,
Text = text
};
PostMessage(payload);
}
public HttpResponseMessage PostMessage(Payload payload)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
string payloadJson = JsonConvert.SerializeObject(payload);
var content = new StringContent(payloadJson, Encoding.UTF8, "application/json");
using (HttpClient client = new HttpClient())
{ var result = client.PostAsync(_uri, content).Result; return result; }
}
}
//This class serializes into the Json payload required by Slack Incoming WebHooks
public class Payload
{
[JsonProperty("channel")]
public string Channel { get; set; }
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
And here is where I actually call the PostMessage (I've hidden by actual webhook address/token for security purposes)
public void SlackMessageTest()
{
string WebHookUrl = "https://myslackwebsite.slack.com/services/MYWEBHOOKURLFROMSLACK";
SlackClient client = new SlackClient(WebHookUrl);
client.PostMessage(username: "tester", text: "Testing Slack Integration!", channel: "#random");
}
The error I get is as follows:
The request was aborted: Could not create SSL/TLS secure channel.
So it seems I have an issue with my PostMessage method, with the URI return. From what I've researched, it should just work! My web hook is validated and set up correctly in Slack.
Any help is much appreciated!!
Slack requires TLS 1.2 and above
That said, replace SecurityProtocolType.Tls (TLS 1) with SecurityProtocolType.Tls12(TLS 1.2)
REF: SecurityProtocolType Enum
Hth.

Can't get response from Ecom Express API when using VerveLogic.HttpHandler

I have to integrate Ecom express shipping API in my code. This api is for pre generation of AWB Number at the time of order. Here is the document instruction to integrate shipping api :
Test Server URL:http://staging.ecomexpress.in/apiv2/fetch_awb/
Test Server Credentials: Username: ecomexpress Password: Ke$3c#4oT5m6h#$
Sample Request Body :
For PPD
username=ecomexpress&password=Ke$3c#4oT5m6h#$&count=1&type=PPD
For COD
username=ecomexpress&password=Ke$3c#4oT5m6h#$&count=1&type=COD
This API works fine with postman and generate AWB number also, But trying with C# code gives null object.
Check here the code I am using :
var client = new HttpHandler.Client("http://staging.ecomexpress.in/apiv2/fetch_awb/");
var newUrl = "http://staging.ecomexpress.in/apiv2/fetch_awb/?username=ecomexpress&password=Ke$3c#4oT5m6h#$&count=1&type=PPD";
var data = client.PostData<dynamic>(newUrl, new { username= "ecomexpress", password= "Ke$3c#4oT5m6h#$", count=1,type= "PPD" });
if (data!=null){ // do some stuff here }
I am using http handler nuget package (https://www.nuget.org/packages/VerveLogic.HttpHandler/)
Please help or suggest a way in which I can get AWB Number using C# code.Also check the postman and document instruction here :
You can easily make this work using HttpClient and Newtonsoft.Json, so might be something with that particular library not sending the parameters as form-urlencoded.
using (var client = new HttpClient())
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", "ecomexpress"),
new KeyValuePair<string, string>("password", "Ke$3c#4oT5m6h#$"),
new KeyValuePair<string, string>("count", "1"),
new KeyValuePair<string, string>("type", "PPD"),
});
var response = await client.PostAsync("http://staging.ecomexpress.in/apiv2/fetch_awb/", content);
var body = await response.Content.ReadAsStringAsync();
var responseObject = JsonConvert.DeserializeObject<ResponseObject>(body);
}
Where ResponseObject is:
public class ResponseObject
{
[JsonProperty("reference_id")]
public int ReferenceId { get; set; }
[JsonProperty("success")]
public string SuccessText { get; set; }
[JsonIgnore]
public bool Success => SuccessText.Equals("yes", StringComparison.OrdinalIgnoreCase);
[JsonProperty("awb")]
public int[] Awb { get; set; }
}

Can't get a valid response from a REST web service using System.Net.Http.HttpClient

I am using this test method (and helper class) to verify the response from an external web service:
[TestMethod]
public void WebServiceReturnsSuccessResponse()
{
using (var provider = new Provider(new Info()))
using (var result = provider.GetHttpResponseMessage())
{
Assert.IsTrue(result.IsSuccessStatusCode);
}
}
private class Info : IInfo
{
public string URL { get; set; } =
"https://notreallythe.website.com:99/service/";
public string User { get; set; } = "somename";
public string Password { get; set; } = "password1";
}
I can't get this test to pass; I always get a 500 - Internal Server Error result. I have connected via an external utility (Postman) - so the web service is up and I can connect with the url & credentials that I have.
I think the problem is in my instantiation of the HttpClient class, but I can't determine where. I am using Basic authentication:
public class Provider : IProvider, IDisposable
{
private readonly HttpClient _httpClient;
public Provider(IInfo config){
if (config == null)
throw new ArgumentNullException(nameof(config));
var userInfo = new UTF8Encoding().GetBytes($"{config.User}:{config.Password}");
_httpClient = new HttpClient
{
BaseAddress = new Uri(config.URL),
DefaultRequestHeaders =
{
Accept = { new MediaTypeWithQualityHeaderValue("application/xml")},
Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(userInfo)),
ExpectContinue = false,
},
};
}
public HttpResponseMessage GetHttpResponseMessage()
{
return _httpClient.GetAsync("1234").Result;
}
}
The response I get back appears to go to the correct endpoint; the RequestUri in the response looks exactly like I expect, https://notreallythe.website.com:99/service/1234.
You need to load up Fiddler and do a recording of the HTTP traffic when this operation succeeds (through the browser).
Then, load up your code, stand up another instance (or window) of Fiddler, and do the same thing with your code. Now, compare the two Fiddler windows to see what is different.
You only need to compare those things in Fiddler that are highlighted in blue. You can ignore the other communications.

Categories