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.
Related
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.
I am trying to send chars like : / . in asp.net mvc 5 to an API controller endpoint, but it fails as soon as I try something containing certain chars. For example, I can't send message:hi, I have to change it to message_hi to get it working.
I am trying to send an email using Exchange and the body (containing an URL and other info) won't go through.
My API Controller:
[Route("send/{adress}/{subject}/{body}")]
public void SendEmail(string adress, string subject, string body)
{
Office365MailSender ms = new Office365MailSender();
EmailDto email = new EmailDto(adress, subject, body);
ms.Send(email);
}
Calling the above endpoint from my application:
public static async Task<string> SendMail(IPhoneCall phoneCall)
{
var email = new EmailEntity(phoneCall);
using (var client = new HttpClient())
{
var uri = new Uri("http://url/email/send/" + email.Recipient + "/" + email.Title + "/" + email.body);
var msg = await client.GetAsync(uri);
}
return "Email Sent";
}
An example of a value of the uri variable would be:
http://url/email/send/myemail#outlook.com/Hello There/Hi,\nThis is a url you can use for stuff: https://thisisit.com. \n Thanks bye.
I've tried HttpUtility.UrlEncode on the body before I send it, but that does nothing. Does anyone know how to send strings containing these type of chars?
I would recommend you using the POST verb in order to send the body of the message. So you could start by writing a view model:
public class MailMessageViewModel
{
public string Address { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
that your Web API action will take as parameter:
[Route("send")]
[HttpPost]
public IHttpActionResult SendEmail(MyViewModel model)
{
Office365MailSender ms = new Office365MailSender();
EmailDto email = new EmailDto(model.Address, model.Subject, model.Body);
ms.Send(email);
return this.Ok();
}
and then you could invoke like this:
var email = new EmailEntity(phoneCall);
using (var client = new HttpClient())
{
var uri = new Uri("http://url/email/send");
var content = new StringContent(
JsonConvert.SerializeObject(new
{
Address = email.Recipient,
Subject = email.Title,
Body = email.body,
}),
UnicodeEncoding.UTF8,
"application/json");
var msg = await client.PostAsync(uri, content);
}
I have a web api that I can access successfully through a browser :-
https://127.0.0.1:8443/ncrApi
I am trying to create a simple console program in C# using VS2015 to send data and receive a response using http POST.
Here is what I have so far:-
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace WebSample
{
class ApiSendData
{
public string UserID { get; set;} // username
public string Password { get; set;} // password for the webapi
public string ApiFunction { get; set; }
public string DppName { get; set; }
public string ClearData { get; set; }
public string DppVersion { get; set; }
}
class Program
{
static void Main(string[] args)
{
// The Main function calls an async method named RunAsync
// and then blocks until RunAsyncc completes.
RunAsync().Wait();
}
static async Task RunAsync()
{
using (var client = new HttpClient())
{
//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// This code sets the base URI for HTTP requests,
// and sets the Accept header to "application/json",
// which tells the server to send data in JSON format.
client.BaseAddress = new Uri("https://localhost:8443/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var datatobeSent = new ApiSendData()
{
UserID = "xxxx",
Password = "yyyy",
ApiFunction ="NcrSecureData",
DppName ="CSampleCustomer",
DppVersion ="Latest",
ClearData ="1234567890",
ResultType = "JSON"
};
HttpResponseMessage response = await client.PostAsJsonAsync("ncrApi", datatobeSent);
if (response.IsSuccessStatusCode)
{
// Get the URI of the created resource.
Uri ncrUrl = response.Headers.Location;
// do whatever you need to do here with the returned data //
}
}
}
}
}
In my response variable I get the 200 OK http 1.1 message, with content type = application/json and content-length = 174... but no actual data is received...
the variable ncrUrl is also null....
I am wondering if I need additional statements in my console program to receive the data?
Here is what I have been following:-
http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Upon reading the comments it seems that your api is configured to return a file instead of a string content such as JSON or XML. You can use HttpContent.ReadAsStreamAsync Method to read the response stream and save it to a file.
HttpResponseMessage response = await client.PostAsJsonAsync("ncrApi", datatobeSent);
using (Stream output = File.OpenWrite("filename.txt")) // change
{
using (Stream input = await response.Content.ReadAsStreamAsync())
{
input.CopyTo(output);
}
}
I am posting an object to a WebApi method. I'm using PostAsJsonAsync to do this.
public async Task<HttpResponseMessage> PostAsync(string token, ServiceCall call)
{
var client = new HttpClient();
client.SetBearerToken(token);
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", call);
return response;
}
The object call that I'm passing is not null when I post it.
[HttpPost]
[Route("id/nestedcall")]
public async Task<IHttpActionResult> NestedCall([FromBody]ServiceCall call)
{
// call is null here
}
However it is null in my API method. I can't seem to work out why as all of the examples I've followed use this format.
Why isn't the call object being picked up by the web api?
Edit
Here is the ServiceCall object. It is in a separate class library and a reference is included in both the web application and the API.
public class ServiceCall
{
public ServiceCall(Service service, string grantType)
{
ClientId = service.Id;
ClientSecret = service.Secret;
Uri = service.Uri;
Scope = service.Scope;
GrantType = grantType;
}
public ServiceCall(string clientid, string clientsecret, string uri, string scope, string grantType)
{
ClientId = clientid;
ClientSecret = clientsecret;
Uri = uri;
Scope = scope;
GrantType = grantType;
}
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string Uri { get; set; }
public string Scope { get; set; }
public string GrantType { get; set; }
}
I have seen Object null in WebApi method after PostAsJsonAsync due to serialization.
Better to use PostAsync like below :
var obj = new MyClass()
{
MyProperty = 11
};
using (var client = new HttpClient())
{
string inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
HttpContent inputContent = new StringContent(inputJson, Encoding.UTF8, "application/json");
HttpResponseMessage response1 = client.PostAsync("http://localhost:60909/api/home/Test", inputContent).Result;
if (response1.IsSuccessStatusCode)
{
}
}
Using Prefix Stackify I was able to diagnose that the serialiser was throwing an exception:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Core.Models.ServiceCall. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'ClientId', line 1, position 12.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize
However, very helpfully, rather than tell me that an exception occurred the controller simply gave me a null object.
As hinted by the exception the solution is to add a default constructor (or at least one the serialiser can understand).
public ServiceCall()
{
}
looks like the JSON serialization may be failing. BTW, remove that [FromBody] and try without it like below. PostAsJsonAsync method serializes the ServiceCall object to JSON and then sends the JSON payload in a POST request.
public async Task<IHttpActionResult> NestedCall(ServiceCall call)
{
// your code
}
I run into exactly the same problem and had to do this to solve it:
using (var client = new HttpClient())
{
client.SetBearerToken(token);
var content = new StringContent(JsonConvert.SerializeObject(call), Encoding.UTF8, "application/json");
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", content);
return response;
}
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; }
}