How to use IUserIdProvider - signalr

My code:
public string GetUserId(IRequest request) {
var token = request.QueryString.Get("token");
// what is it? request.User.Identity.Name;
if (string.IsNullOrWhiteSpace(token)) {
return token;
}
else {
return JsonConvert.SerializeObject(new UserAbility().GetUserByToken(token));
}
}
I need to map the connection with the user using a different identifier.
So i want to get the custom token from the QueryString in this method, but GetUserId doesn't trigger in every reqeust.
And i always get the request.User.Identity.Name is string empty?

This article explains what you need to do.
http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#IUserIdProvider

Related

Slack-Integration in ASP.NET Web-Api 2

I want to know exactly why this is not working:
[HttpPost]
public IHttpActionResult Post(Slack_Webhook json)
{
return Ok(json.challenge);
}
public class Slack_Webhook
{
public string type { get; set; }
public string token { get; set; }
public string challenge { get; set; }
}
The Official Documentation says:
We’ll send HTTP POST requests to this URL when events occur. As soon
as you enter a URL, we’ll send a request with a challenge parameter,
and your endpoint must respond with the challenge value.
This is an example object (JSON) sent by Slack:
{
"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
"type": "url_verification"
}
EDIT:
I could write a book on code that does not work in this issue... here's another example that did not work - still no idea what is wrong:
[HttpPost]
public IHttpActionResult Post()
{
var pairs = Request.GetQueryNameValuePairs();
bool isValidToken = false;
string c = "This does not work.";
foreach(var pair in pairs)
{
if (pair.Key == "token")
{
if (pair.Value == "<UNIQUETOKEN>")
{
isValidToken = true;
}
}
if (pair.Key == "challenge")
{
c = pair.Value;
}
}
if (isValidToken == true)
{
return Json(new {challenge = c });
}
else
{
return BadRequest();
}
}
EDIT2:
Very interesting that I get NULL as a response from below code - that means the body of the received POST is empty.. Could anyone with a working Slack-Integration try that out? So their site is wrong, stating the challenge is sent in the body - where else could it be?
// POST: api/Slack
[HttpPost]
public IHttpActionResult Post([FromBody]string json)
{
return Json(json);
}
EDIT3:
This function is used to get the raw request, but there is nothing inside the body - I am out of solutions.. the support of Slack said, they have no idea about ASP.NET and I should ask here on SO for a solution. Here we are again! ;-)
[HttpPost]
public async Task<IHttpActionResult> ReceivePostAsync()
{
string rawpostdata = await RawContentReader.Read(this.Request);
return Json(new StringContent( rawpostdata));
}
public class RawContentReader
{
public static async Task<string> Read(HttpRequestMessage req)
{
using (var contentStream = await req.Content.ReadAsStreamAsync())
{
contentStream.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(contentStream))
{
return sr.ReadToEnd();
}
}
}
}
The result ( as expected ) looks like this:
Our Request:
POST
"body": {
"type": "url_verification",
"token": "<token>",
"challenge": "<challenge>"
}
Your Response:
"code": 200
"error": "challenge_failed"
"body": {
{"Headers":[{"Key":"Content-Type","Value":["text/plain; charset=utf-8"]}]}
}
I think I'm missing something - is there another way to get the body of the POST-Request? I mean, I can get everything else - except the body ( or it says it is empty).
EDIT4:
I tried to read the body with another function I found - without success, returns empty string - but to let you know what I already tried, here it is:
[HttpPost]
public IHttpActionResult ReceivePost()
{
var bodyStream = new
StreamReader(HttpContext.Current.Request.InputStream);
bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
var bodyText = bodyStream.ReadToEnd();
return Json(bodyText);
}
While trying to solve this I learnt a lot - but this one seems to be so impossible, that I think I will never solve it alone. Thousands of tries with thousands of different functions - I have tried hundreds of parameters and functions in all of WebApi / ASP.NET / MVC / whatever - why is there no BODY? Does it exist? What's his/her name? Where does it live? I really wanna hang out with that parameter if I ever find it, must be hidden at the end of the rainbow under a pot of gold.
If you can use ASP.NET Core 2, this will do the trick:
public async Task<ActionResult> HandleEvent([FromBody] dynamic data)
=> new ContentResult {Content = data.challenge};
According to the official documentation linked to in the OP you have to format your response depending on the content type you return.
It is possible you are not returning the value (challenge) in one of the expected formats.
Once you receive the event, respond in plaintext with the challenge
attribute value. In this example, that might be:
HTTP 200 OK
Content-type: text/plain
3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P
To do the above you would have needed to return your request differently
[HttpPost]
public IHttpActionResult Post([FromBody]Slack_Webhook json) {
//Please verify that the token value found in the payload
//matches your application's configured Slack token.
if (ModelState.IsValid && json != null && ValidToken(json.token)) {
var response = Request.CreateResponse(HttpStatusCode.OK, json.challenge, "text/plain");
return ResponseMessage(response);
}
return BadRequest();
}
Documentation also shows
Or even JSON:
HTTP 200 OK
Content-type: application/json
{"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"}
Which again would have to be formatted a little differently
[HttpPost]
public IHttpActionResult Post([FromBody]Slack_Webhook json) {
//Please verify that the token value found in the payload
//matches your application's configured Slack token.
if (ModelState.IsValid && json != null && ValidToken(json.token)) {
var model = new { challenge = json.challenge };
return Ok(model);
}
return BadRequest();
}
Here's how you can access the data:
[HttpPost]
[Route("something")]
public JsonResult DoSomething()
{
var token = HttpContext.Request.Form["token"];
// Is the same as:
// var token = Request.Form["token"];
return new JsonResult(token);
}
I suggest using a Request Bin for further debugging.

FromBody value get null in web api

this is my post method in apiController
[HttpPost]
public String Post([FromBody]String key)
{
Users ws;
try
{
ws = JsonConvert.DeserializeObject<Users>(key);
// return "success "+ key;
return db.InsertFineInfo(ws);
}
catch (Exception ex)
{
return "ERROR Testing Purposes: " + ex;
}
}
This is part of my model calss.(Users class)there are many attributes but here i have mentioned only few of em with getters and setters
{
private String UserID;
private String UserName;
private String UserHeight;
private String UserWeight;
private String UserBMI;
private String RequiredNeutrition;
public string UserID1
{
get
{
return UserID;
}
set
{
UserID = value;
}
}
i tried to call this post method using postmen .in every attempt i get a null value for key .
this is how i tried the post method with one header parameter application/json
what went wrong ? something wrong with method or the way i try to call it?
OK a couple points...
Firstly the JSON your method would be expecting would look like
{
"key": "your string....."
}
Secondly the code you have supplied is a bit counter intuitive... Why not simply have
[HttpPost]
public String Post([FromBody]Users ws)
{
... // Done ?
}
You need to publish more code for me to be able to give you a correct answer as to what the JSON would look like that would be accepted by the above method.
In Web API when the parameter comes through as null you can be pretty sure that the JSON sent to the method does not match the JSON generated when you serialize the parameter to a JSON string.
You have to model your input as a C# class, and then take that type as an input.
Assuming that you already have a "User" class, with the same properties as the JSON, that you send in the request body:
[HttpPost]
public String Post([FromBody]User user)
{
try
{
return db.InsertFineInfo(user);
}
catch (Exception ex)
{
return "ERROR Testing Purposes: " + ex;
}
}

How to deserialize

How to deserialize Task response using Json .
public HttpResponseMessage Put(int id, ModelClass modelbject)
{
if (ModelState.IsValid && id == modelbject.modelbjectID)
{
db.Entry(modelbject).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
I want to derialize this and check the IsSuccessStatusCode in my class where i am calling this put method. How can i achieve ?
I want to derialize this and check the IsSuccessStatusCode in my class where i am calling this put method.
You don't have to "deserialize" anything. The method returns an HttpResponseMessage, which has the property you're looking for.
var result = yourController.Put(someId, someObject);
var success = result.IsSuccessStatusCode;
Perhaps the fact that this is a web application is adding some confusion to how you're picturing it. But if you have a class which directly calls this method, then what you get back is simply an HttpResponseMessage object. Which can be inspected just like any other object. No actual web layer is involved in that interaction.

Adding username to groups in SignalR

Is it possible to use the IUserIDProvider instead of ConnectionID when working with Groups? I have already found an answer here, but that concerns the SignalR 1.0 version. I wonder, whether things have changed in 2.0.
So far, I was using the conventional
Groups.Add(Context.ConnectionId, "groupName");
However, it was difficult to keep track of the connected users when their connectionID was changed (the client is a Xamarin Android app and somehow, reconnection always resulted in creation of a new ConnectionID). Thus, when the client is connecting, I have added a header:
public async Task<bool> Login(int waitMilis, string name)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(waitMilis);
_connection.Headers.Add("userName", name);
await _connection.Start();
return true;
}
catch(Exception ex)
{
CallFailure(ex);
return false;
}
}
And on server side, implemented the IUserIdProvider:
public class MyUserProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
if (request == null)
throw new ArgumentNullException("request");
else if (request.Headers != null && request.Headers["userName"] != null)
return request.Headers["userName"].ToString();
else return null;
}
}
Now, I would like to do something like
Groups.Add("userName", "groupName");
but the Add method does not have an overload for IUserIdProvider. So, is there a possibility to combine the IUserIdProvider and working with Groups, or am I stuck to creating a ConcurrentDictionary and then calling this?
foreach(User user in group.Users)
{
Clients.User(user.Name).SendMessage(message,
group.LastUpdateIndex
);
}
It ruins the whole beauty and simplicity of the SignalR code :-/
Unfortunately, there isn't currently a method like Groups.Add("userName", "groupName"); in SignalR.
I suggest adding users to their appropriate group(s) in OnConnected:
public class MyHub : Hub
{
public override async Task OnConnected()
{
var userName = MyUserHelper.GetUserId(Context.Request);
foreach (var groupName in GroupManager.GetJoinedGroups(userName))
{
await Groups.Add(Context.ConnectionId, groupName);
}
}
// ...
}
If you need to add an already connected user to a group, then you will likely need to send a message to the user using something like Clients.User(userName).joinGroup(groupName). Each client with userName could then call the appropriate hub method to join groupName.

In asp.net is there a function to validate an email address?

I've noticed that if you try to send an email to an invalid address, an exception is raised:
MailAddress To=new MailAddress("invalidemailaddress","recipientname");
throws:
"The specified string is not in the form required for an e-mail address"
This means that there must be a .Net function which is executed in MailAddress to check if the email address is valid or not. Is there a way to call this 'validate' function directly? This way I won't need to create my own IsValid function.
No but you can make one:
public bool ValidateEmailAddress (string email)
{
if (string.IsNullOrEmpty (email)) return false;
try
{
MailAddress to = new MailAddress (email);
return true;
}
catch (WhateverException e)
{
return false;
}
}
Answering comments. I am aware this technique is regarded as a bad one and with reason. What I would like to point out is that this approach will give you 100% guarantee the .NET mailing library will be able to send to a validated address lately. The problem with Regexes (of which there are plenty) is that each one addresses one particular subset of the set of technically correct addresses as per specification. One would be narrower, the other one would be wider than the subset defined internally in .NET. If you were to use Regex validation, then in the first case your Regex would cut off a portion of the valid addresses (as seen by .NET), in the latter case the validaton will let through addresses that the .NET mailing library won't treat as invalid per its own internal validation. The one true way to make sure you valid set 100% matches the .NET set (or of any other third party library you would use) is to fall for the try/catch approach, unless of course this third party library offers some validation method already.
Yes, there is such a .Net function, but its functionality is unaccessible by "standard" means: MailAdress uses a private ParseAddress method, which in turn uses System.Net.Mime.MailBnfHelper. The latter is an internal class, so it's not (easily) accessible outside the framework itself.
Thus, the only way to use these functions would be to use reflection, which I strongly advise against. Since these functions are undocumented and unaccessible without reflection, their implementation might change and your code might break in future versions of the framework.
There's a good example of an email validation function on CodeProject.
Original Source Code written by Vasudevan Deepak Kumar:
public static bool isEmail(string inputEmail)
{
inputEmail = NulltoString(inputEmail);
string strRegex = #"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}" +
#"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
#".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
Regex re = new Regex(strRegex);
if (re.IsMatch(inputEmail))
return (true);
else
return (false);
}
Unfortunately, there is no way to get at that functionality without reverse-engineering it or using that specific exception, sadly.
The traditional way to validate an email address has always been with regular expressions, but there are lengths you can go beyond that to validate emails even further, if you so wish:
The Forgotten Art of Email Address Validation
You could write your own class:
class EmailAddress
{
private MailAddress _email;
public string Address
{
get
{
return _email == null ? string.Empty : _email.Address;
}
}
public string DisplayName
{
get
{
return _email == null ? string.Empty : _email.DisplayName;
}
}
public string Host
{
get
{
return _email == null ? string.Empty : _email.Host;
}
}
public string User
{
get
{
return _email == null ? string.Empty : _email.User;
}
}
public EmailAddress(string email)
{
try {
_email = new MailAddress(email);
}
catch (Exception) {
_email = null;
}
}
public EmailAddress(string email, string displayName)
{
try {
_email = new MailAddress(email, displayName);
}
catch (Exception) {
_email = null;
}
}
public EmailAddress(string email, string displayName, Encoding displayNameEncoding)
{
try {
_email = new MailAddress(email, displayName, displayNameEncoding);
}
catch (Exception) {
_email = null;
}
}
public bool IsValid()
{
return _email == null ? false : true;
}
public override string ToString()
{
return this.Address;
}
}
Now you use it just as MailAddress but there is now no exception when the Email address is not valid. Instead you call the IsValid method:
var email = new EmailAddress("user#host.com");
if (email.IsValid()) {
...
}
else {
...
}

Resources