For ASP.NET 5 beta 6, when creating a controller it seems you no longer extend Controller (Microsoft.AspNet.Mvc.Controller). So how would I get access to the current user? Previously I just used the "User" property off Controller to get the System.Security.Claims.ClaimsPrincipal for user associated with the executing action. Without Controller, now "User" is no longer defined.
Works in Beta 5:
[Authorize]
[Route("[controller]")]
public class UserDetailsController : Controller
{
[HttpGet]
public JsonResult Get()
{
var user = User as ClaimsPrincipal;
var claims = from c in user.Claims
select new
{
type = c.Type,
value = c.Value
};
return new JsonResult(claims);
}
}
Does not work in Beta 6:
[Authorize]
[Route("[controller]")]
public class UserDetailsController
{
[HttpGet]
public JsonResult Get()
{
var user = User as ClaimsPrincipal; // ERROR - The name 'User' does not exist in the current context
var claims = from c in user.Claims
select new
{
type = c.Type,
value = c.Value
};
return new JsonResult(claims);
}
}
So I spent longer than I want to admit tonight searching for the answer to how I can get access to the current user using the new POCO style controllers and of course soon after I posted the question I stumbled across the answer while reading through ASP.NET's announcements repo. Hopefully this helps someone else out there.
The POCO styled controller should be:
[Authorize]
[Route("[controller]")]
public class UserDetailsController
{
[ActionContext]
public ActionContext ActionContext { get; set; }
public HttpContext HttpContext => ActionContext.HttpContext;
[HttpGet]
public JsonResult Get()
{
var user = HttpContext.User;
var claims = from c in user.Claims
select new
{
type = c.Type,
value = c.Value
};
return new JsonResult(claims);
}
}
In beta6 the Controller base class no longer comes with the Microsoft.AspNet.Mvc.Core package, but with the Microsoft.AspNet.Mvc.ViewFeatures package.
So adding a reference to Microsoft.AspNet.Mvc.ViewFeatures will fix your issue. The Controller class still lives in the Microsoft.AspNet.Mvc namespace.
Also see this announcement: Refactoring of MVC packages.
Related
I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
You could use an HttpClient instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public async Task<IActionResult> GetByIdAync(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteAsync(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository in your sample:
public RankingRepository: IRankingRepository
{
public Task<LeagueTableModel> GetRankingsAsync(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteAsync(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public Task<IActionResult> GetByIdAsync(long id)
//Here is where I want to make the requisition
var model = await _rankingRepository.GetRankingsAsync(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
I want to populate an object using methods in a Controller class. So I've created the object in the Controller class and then tried to populate it using methods in the class. This does not work though, because everytime a method in a controller is called, the entire Controller class is reinitiated. So I get a brand new object everytime I try to populate the object. However, I don't know how else I can create an object and populate it. I've added the code below. The object I'm talking about is ProcessModel. Ignore the other objects I created at the top of the class.
Controller:
public class HomeController : Controller
{
ProcessModel pm = new ProcessModel();
RetrievePatterns pt = new RetrievePatterns();
RetrieveModel rm = new RetrieveModel();
public IActionResult Index()
{
FindPatterns fp = new FindPatterns(rm.pm, pt.KpiPatterns);
ViewData["KPIs"] = fp.passdata;
return View();
}
[HttpPost]
public IActionResult AddEvent([FromBody] Event data)
{
data.ID = pm.EventObjects.Count + 1;
pm.EventObjects.Add(data);
return Json(pm.EventObjects.Count);
}
[HttpPost]
public IActionResult AddProcessName(string data)
{
pm.ID = 1;
pm.Name = data;
return Json(new { title = pm.Name });
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public ActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Object:
public class ProcessModel
{
public List<Event> EventObjects = new List<Event>();
public List<Entity> EntityObjects = new List<Entity>();
public int ID { get; set; }
public string Name { get; set; }
}
You could create a Singleton class which is responsible to create only one instance from Process model;
public class ProcessModelSingleton
{
private static ProcessModel _processModel = new ProcessModel();
private ProcessModelSingleton()
{
}
public static ProcessModel Instance
{
get { return _processModel; }
}
}
Then assign it a global variable in the controller;
ProcessModel pm = ProcessModelSingleton.Instance;
EDIT
Created instance by singleton class shared by all users. So, if you want to make it user specific, using Session is the best option.
public ProcessModel pm {
get
{
if (Session["ProcessModel"] == null)
{
Session["ProcessModel"] = new ProcessModel();
}
return (ProcessModel)Session["ProcessModel"];
}
}
Keep in mind static variables are shared across all users for that web server. So data populated into a singleton class by one user's request can be read/overwritten by a different user's request. This may be what you are after, but if not you should look into Session State.
ASP.NET Session State Overview
As HTTP is stateless by nature, usage of sessions are discouraged my Microsoft for Asp.NET. By default they are only visible to the web server that creates them so if you are using a farm, you either need to look at server affinity (also called sticky sessions) with a load balancer or out of proc state (such as SQL Server or NCache).
Ideally your application design should avoid the need for sessions and carry only the necessary data between requests via cookies and/or query strings. For more complex applications however this is usually not possible and so Session State is used.
I have the custom AuthorizeAttribute where I need to use one of the business layer services to validate some data in the database before giving user a permission to view the resource. In order to be able to allocate this service within the my AuthorizeAttribute I decided to use service location "anti-pattern", this is the code:
internal class AuthorizeGetGroupByIdAttribute : AuthorizeAttribute
{
private readonly IUserGroupService _userGroupService;
public AuthorizeGetGroupByIdAttribute()
{
_userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();
}
//In this method I'm validating whether the user is a member of a group.
//If they are not they won't get a permission to view the resource, which is decorated with this attribute.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
int groupId = int.Parse(parameters["groupId"]);
int currentUserId = HttpContext.Current.User.Identity.GetUserId();
return _userGroupService.IsUserInGroup(currentUserId, groupId);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContex)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actionContex);
}
else
{
actionContex.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
I have couple of other attributes like this in my application. Using service locator is probably not a good approach. After searching the web a little bit I found some people suggesting to use IAuthorizationFilter with dependency injection instead. But I don't know how to write this kind of IAuthorizationFilter. Can you help me writing IAuthorizationFilter that will do the same thing that the AuthorizeAttribute above?
So after struggling for a while I think I managed to resolve this issue. Here are the steps you have to do in order to that:
1) First you have to make GetGroupByIdAttribute passive, and by passive I mean an empty attribute without any logic within it (it will be used strictly for decoration purposes)
public class GetGroupByIdAttribute : Attribute
{
}
2) Then you have to mark a controller method, for which you want to add authorization, with this attribute.
[HttpPost]
[GetGroupById]
public IHttpActionResult GetGroupById(int groupId)
{
//Some code
}
3) In order to write your own IAuthorizationFilter you have to implement its method ExecuteAuthorizationFilterAsync. Here is the full class (I included comments to guide you through the code):
public class GetGroupByIdAuthorizationFilter : IAuthorizationFilter
{
public bool AllowMultiple { get; set; }
private readonly IUserGroupService _userGroupService;
//As you can see I'm using a constructor injection here
public GetGroupByIdAuthorizationFilter(IUserGroupService userGroupService)
{
_userGroupService = userGroupService;
}
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
//First I check whether the method is marked with the attribute, if it is then check whether the current user has a permission to use this method
if (actionContext.ActionDescriptor.GetCustomAttributes<GetGroupByIdAttribute>().SingleOrDefault() != null)
{
Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
int groupId = int.Parse(parameters["groupId"]);
int currentUserId = HttpContext.Current.User.Identity.GetUserId();
//If the user is not allowed to view view the resource, then return 403 status code forbidden
if (!_userGroupService.IsUserInGroup(currentUserId, groupId))
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.Forbidden));
}
}
//If this line was reached it means the user is allowed to use this method, so just return continuation() which basically means continue processing
return continuation();
}
}
4) The last step is to register your filter in the WebApiConfig.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Here I am registering Dependency Resolver
config.DependencyResolver = ServiceLocator.Instance.DependencyResolver;
//Then I resolve the service I want to use (which should be fine because this is basically the start of the application)
var userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();
//And finally I'm registering the IAuthorizationFilter I created
config.Filters.Add(new GetGroupByIdAuthorizationFilter(userGroupService));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Now, if needed, I can create additional IActionFilters that use IUserGroupService and then inject this service at the start of the application, from WebApiConfig class, into all filters.
Perhaps try it like shown here:
Add the following public method to your class.
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// gets the dependecies from the serviceProvider
// and creates an instance of the filter
return new GetGroupByIdAuthorizationFilter(
(IUserGroupService )serviceProvider.GetService(typeof(IUserGroupService )));
}
Also Add interface IFilterMetadata to your class.
Now when your class is to be created the DI notices that there is a CreateInstance method and will use that rather then the constructor.
Alternatively you can get the interface directly from the DI in your method by calling
context.HttpContext.Features.Get<IUserGroupService>()
I've got a custom claim added to my ApplicationUser class as follows:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
if(Theme != null)
userIdentity.AddClaim(new Claim("ThemeBundle", Theme.Bundle));
return userIdentity;
}
public int? ThemeId { get; set; }
[ForeignKey("ThemeId")]
public virtual Theme Theme { get; set; }
}
I extended Identity like this:
public static class IdentityExtensions
{
public static string GetThemeBundle(this IIdentity identity)
{
var claim = ((ClaimsIdentity) identity).FindFirst("ThemeBundle");
// Test for null to avoid issues during testing
return (claim != null) ? claim.Value : string.Empty;
}
}
I update the model behind the claim from the following Action Method:
public ActionResult ChangeTheme(int id)
{
var theme = _db.Themes.Find(id);
if (theme == null)
return HttpNotFound();
var userId = User.Identity.GetUserId();
var user = _db.Users.Find(userId);
user.Theme = theme;
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
I access it in a view (or elsewhere) like this:
User.Identity.GetThemeBundle()
When the user updates their "Theme" property with the "ChangeTheme" action, nothing happens until they log off and log back in.
I've spent all day mulling over more than the following QA's with no good result:
Update User Claim not Taking Effect. Why?
MVC 5 AddToRole requires logout before it works?
And thanks to #Brendan Green: ASP.NET Identity - Forcing a re-login with security stamp
So far the best I've got is that the page will refresh and the claim returns an empty string instead of the desired result, OR I redirect the user to the login screen. At least those are less ambiguous than nothing happening.
How on earth can I get the claim to update globally as soon as the user changes their "Theme" property? I'd settle for a good way to fully log the user off and back on if needed. Using the AuthenticationManager.Signout and .Signin methods doesn't quite do the trick.
As of Asp.Net MVC 6 and Asp.Identity 3.0.0-rc1-final you could use Task SignInManager<TUser>.RefreshSignInAsync(TUser user); in order to do that.
I am trying to create(Or get an instance of it somehow) for Microsoft.AspNet.Mvc.Rendering.DefaultHtmlGenerator inside my MVC6 controller method
I wanted to generate the html for validation for my Model my self inside my controller of asp.net mvc. My issue is where to get the constructor data for DefaultHtmlGenerator like antiforgery, metadataProvider..etc
[HttpGet]
public IActionResult GetMarkup()
{
// IHtmlGenerator ge = this.CurrentGenerator();
IHtmlGenerator ge = new DefaultHtmlGenerator(params);
var tag= ge.GetClientValidationRules(params)
}
here is the a link about the HtmlGenerator class
DefaultHtmlGenerator
Since MVC 6 is based on dependency injection, all you have to do is require IHtmlGenerator in your constructor, and the DI container will automatically fill in all of the dependencies of DefaultHtmlGenerator (provided that is what is setup in your DI configuration).
public class HomeController : Controller
{
private readonly IHtmlGenerator htmlGenerator;
public HomeController(IHtmlGenerator htmlGenerator)
{
if (htmlGenerator == null)
throw new ArgumentNullException("htmlGenerator");
this.htmlGenerator = htmlGenerator;
}
public IActionResult GetMarkup()
{
// Use the HtmlGenerator as required.
var tag = this.htmlGenerator.GetClientValidationRules(params);
return View();
}
}
That said, it appears that the GetClientValidationRules method is only designed to work within a view, since it accepts ViewContext as a parameter. But this does answer the question that you asked.