I am trying to setup MVCMailer to use the Email and UserId that were setup when they registered with the website. I used the stock MVC framework to setup the user registration framework and thus the ASPNETDB.MDF DB is used to keep track of email userID and password. I am stuck at the moment on how to pull the current user id and email out of the DB so MVCMailer knows where to send the email with the password reset instructions. Below is my attempt at getting the data out of the DB and into the send mail function. I know I am wrong because I have red squiggles below the word membership.
public virtual MailMessage Welcome(string ADID)
{
var mailMessage = new MailMessage{Subject = "Welcome"};
mailMessage.To.Add(membership.Email);
ViewBag.Name = membership.UserId;
PopulateBody(mailMessage, viewName: "Welcome");
return mailMessage;
}
Does anyone have any idea how to do this I have been searching for a couple hours now on how to accomplish this with no luck. The tutorials I have looked at all seem to use hard coded values for where to send an email. Please let me know if you need any other code and Thanks for your help!
You could fetch it by querying your membership provider:
public virtual MailMessage Welcome(string ADID)
{
var mailMessage = new MailMessage{ Subject = "Welcome" };
var user = Membership.GetUser();
mailMessage.To.Add(user.Email);
ViewBag.Name = user.UserName;
PopulateBody(mailMessage, viewName: "Welcome");
return mailMessage;
}
another possibility is to send the email from a controller action and thus pass the information about the currently connected user as parameter:
using Mvc.Mailer;
public class HomeController: Controller
{
private readonly UserMailer _mailer = new UserMailer;
public ActionResult Index()
{
return View();
}
public ActionResult SendEmailToNewUser()
{
var user = Membership.GetUser();
_mailer.Welcome(user.Email, user.UserName).Send();
return View();
}
}
Related
I have users that will be directed to my application from an external site, and during this redirecting, user's email addresses will be sent to me by HTTP POST.
What I'm trying to accomplish is to receive user's email address, and send the user to the Index view.
I have the following methods:
[HttpGet]
public ActionResult Index()
{
string email = "";
string[] keys = Request.Form.AllKeys;
for (int i = 0; i < keys.Length; i++)
{
Response.Write(keys[i] + ": " + Request.Form[keys[i]] + "<br>");
System.Diagnostics.Debug.WriteLine(keys[i] + ": " + Request.Form[keys[i]]);
email = Request.Form[keys[i]].ToString();
}
return Index(email);
}
[HttpPost]
public ActionResult Index(string email)
{
return View();
}
And this is the method I have to mimic the external site's job, which was redirecting user to my view with user's email address.
public ActionResult Httppost()
{
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["email"] = "test#email.com";
var response = wb.UploadValues("http://localhost:57695/Home/Index", "POST", data);
}
return RedirectToAction("Index");
}
The problem is, Index never receives the email address from my Httppost() method, and email is always received as "". How can I receive the email from Httppost() into Index()?
Why are you sending the email address in a separate request? These two requests have nothing to do with one another. The data received in one isn't available to the other, and they're not guaranteed to happen in the same order.
Instead of this:
var response = wb.UploadValues("http://localhost:57695/Home/Index", "POST", data);
return RedirectToAction("Index");
Just send the value in one request so that it will be available in that request:
return RedirectToAction("Index", new { email = "test#email.com" });
Then you only need one action:
[HttpGet]
public ActionResult Index(string email)
{
// use the email parameter
}
Side note: I don't think this is going to do what you expect:
return Index(email);
That's going to look for a view named after the value of the email variable. Because of how method overloading works, you can't really use a string by itself as a model. You're going to want to either set it on an actual model or put it in something like ViewBag instead.
I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()
I have a .NET Web API project that users the individual accounts. I can register users fine using the standard template AccountController. However, I now want to set up roles and add users to roles depending on the type of user.
There are no roles automatically set up in the DB. How do I set up the roles and how do I add users to the roles?
The only information I can find on this is based on the old ASP.NET Membership, so it fails on the fact that the stored procedures are not set up for it.
Have scoured forums and tutorials on MSDN and can't seem to find an example for Web API.
You can add roles using the RoleManager...
using (var context = new ApplicationDbContext())
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
await roleManager.CreateAsync(new IdentityRole { Name = "Administrator" });
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "admin" };
await userManager.CreateAsync(user);
await userManager.AddToRoleAsync(user.Id, "Administrator");
}
You're right that documentation is a bit light right now. But I find that once you've worked with the RoleManager and the UserManager a bit, the API's are pretty discoverable (but perhaps not always intuitive and sometimes you have to run queries directly against the store or even the db context).
It took me awhile to figure out but I finally got it. Anthony please excuse me but going to repost a lot of your code so that dumb developers like me can understand.
In the latest WebAPI2 (Visual Studio 2013 Update 2) the registration method will look like so:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
What you want to do is replace it with this:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result;
using (var context = new ApplicationDbContext())
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
await roleManager.CreateAsync(new IdentityRole() { Name = "Admin" });
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
result = await UserManager.CreateAsync(user, model.Password);
await userManager.AddToRoleAsync(user.Id, "Admin");
}
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Now when you post it should correctly work, but you may run into a further problem. After I did this my response complained about the DB.
The model backing the <Database> context has changed since the database was created
To fix this error I had to go into the Package Manager Console and enable Migrations.
Enable-Migrations –EnableAutomaticMigrations
Then:
Add Migration
Finally:
Update-Database
A good post on enabling migrations here:
http://msdn.microsoft.com/en-us/data/jj554735.aspx
I am working in Asp.net MVC and have a peculiar requirement for which I have no idea.
What I want is that when a user request a particular URL from my site, I want to visit some preset URL in the database and extract some data and bind them to View before rendering.
For example, If you visit mysite.com/Search/Index, then in my action method Index, i want to visit the anothersite.com/someparticular/url, extract the value in <div> with id="searclbl", bind it to my view and render the page.
I need to read the HTML because the sites I am working with don't offer any Web services or RSS.
Any sort of help or guidance in this matter is appreciated.
I believe you might be able to pull this off using HtmlAgilityPack (which can be installed via a NuGet package inside your project).
For example:
Let’s assume your Index View of the SearchController is strongly typed to the following ViewModel:
public class SearchViewModel
{
public string DivElement { get; set; }
//other properties...
}
This is the Index ActionResult():
public ActionResult Index()
{
var model = new SearchViewModel();
model.DivElement = GetDivFromWebSite();
return View(model);
}
The GetDivFromWebSite() method is where I use HtmlAgilityPack to fetch information from another web site and is defined like so:
private string GetDivFromWebSite()
{
var baseUrl = new Uri("http://www.anotherdomaine.com");
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
using (WebClient client = new WebClient())
{
document.Load(client.OpenRead(baseUrl));
}
if (document == null) return "nothing found!";
var div = document.DocumentNode.SelectNodes("//div[#id='missing-category']").FirstOrDefault();
return div.InnerHtml;
}
This might do the trick!
For our web app I want to let developers create accounts using our api, when an account is created the URI has a subdomain. To accomplish this, do I have to have two separate API's because the URL is different.
For account creation: api.example.com/v1
For account usage: subdomain.example.com/api/v1/
we are using .net mvc3, can this be done with one set of api's and routes?
You should set up routing to get to the relevant controller+action as usual, and inside an action (or much better - using an ActionFilter), examine the subdomain and set the appropriate code that determines the who the user is, for further handling.
Example of a filter would be:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SubdomainFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int? userID = null;
Uri uri = filterContext.HttpContext.Request.Url;
if (uri != null)
{
string domain = uri.Host;
// do whatever you need analyzing the 'domain' variable... like getting the user id according to the subdomain. Let's say we discoveered that the user id is 1.
userID = 1;
}
if (filterContext.ActionParameters.ContainsKey("Subdomain"))
filterContext.ActionParameters["SubdomainUser"] = userID;
base.OnActionExecuting(filterContext);
}
}
and then your action would be:
[SubdomainFilter]
public virtual ActionResult GetUserName(int? userID)
{
...
}