Task
I have a DataMapper class that I use to map data into custom representations for my web api's mobile client.
public class DataMapper
{
public static string Role { get; set; }
public static RoleReturnModel Create(IdentityRole appRole)
{
return new RoleReturnModel
{
Id = appRole.Id,
Name = appRole.Name
};
}
public static CountryReturnModel Create(Country country)
{
return new CountryReturnModel
{
Id = country.Id,
Name = country.Name,
CityList = country.Cities.Select(city => DataMapper.Create(city))
};
}
public static CityReturnModel Create(City city)
{
return new CityReturnModel
{
Id = city.Id,
Name = city.Name,
};
}
}
The first property as you can see is called Role. I need to populate that with whichever role is accessing my web method. This is so because at times I need conditional mapping to return role specific data representations to the client.
Problem
I thought the best place to do DataMapper.Role = CurrentRole would be in the constructor of my ApiController
public class BaseApiController : ApiController
{
private ModelFactory _modelFactory;
private ApplicationUserManager _AppUserManager = null;
private ApplicationRoleManager _AppRoleManager = null;
protected BaseApiController()
{
//Request is null here
DataMapper.Role = Request.GetOwinContext().GetUserManager<ApplicationRoleManager>().FindById(User.Identity.GetUserId()).Name;
}
This however doesn't work . The Request object is null in the constructor. It only gets filled in my actual web method
public class UsersController : BaseApiController
{
IUserRepository UserRepository;
public UsersController() // will use ninject for constructor injection
{
UserRepository = new UserRepository();
}
[Route("profile")]
public IHttpActionResult GetUser()
{
//Request is available here
}
I am a webapi noobie. Need pointers to this problem.
The request is not available as yet in the constructor. You can only access it in an action/method after the controller has already been initialized.
public class BaseApiController : ApiController {
private ModelFactory _modelFactory;
private ApplicationUserManager _AppUserManager = null;
private ApplicationRoleManager _AppRoleManager = null;
protected string GetRole() {
return Request.GetOwinContext()
.GetUserManager<ApplicationRoleManager>()
.FindById(User.Identity.GetUserId()).Name;
}
And accessed
public class UsersController : BaseApiController {
IUserRepository UserRepository;
public UsersController() // will use ninject for constructor injection
{
UserRepository = new UserRepository();
}
[Route("profile")]
public IHttpActionResult GetUser()
{
//Request is available here
var role = GetRole();
}
Or consider extracting that out into an extension method so that it can be reused
var role = this.GetUserRole();
Where
public static string GetUserRole(this ApiController controller) {
var request = controller.Request;
var user = controller.User
return request.GetOwinContext()
.GetUserManager<ApplicationRoleManager>()
.FindById(user.Identity.GetUserId()).Name;
}
Related
public class GroupsController : ControllerBase
{
private readonly ILogger<GroupsController> _logger;
public GroupsController(ILogger<GroupsController> logger)
{
_logger = logger;
string auth = Request.Headers["authorization"];
if (auth is null)
throw new Exception("Missing auth token");
}
[HttpGet("/[controller]/allGroups")]
public List<Group> GetGroups()
{
DbContext dbContext = new DbContext();
List<Group> groups = dbContext.Groups.ToList();
return groups;
}
}
I'm looking to require a authorization header only for this controller, but Request is not possible on the constructor and I don't want to add a auth check on every method on the controller.
Is there a way to check this header on all routes on this controller?
You can carate a custom attribute that validates headers and put it on your action or controller that you want to validate headers. like this:
public class RequiredHeaderAttribute : Attribute, IActionFilter
{
private readonly string _requiredHeader;
public RequiredHeaderAttribute(string requiredHeader)
{
_requiredHeader = requiredHeader;
}
public void OnActionExecuted(ActionExecutedContext context)
{
//
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (!context.HttpContext.Request.Headers.
TryGetValue(_requiredHeader, out _))
{
throw new Exception($"Missing Header Exception: {_requiredHeader}");
}
}
}
Usage:
[RequiredHeader("HeaderName")] //<==NOTE THIS
[HttpGet("/[controller]/allGroups")]
public List<Group> GetGroups()
{
DbContext dbContext = new DbContext();
List<Group> groups = dbContext.Groups.ToList();
return groups;
}
Or
[RequiredHeader("HeaderName")]
public class GroupsController : ControllerBase
{
}
Another way is register RequiredHeader as global filters.
I have base class for every request in my app:
public abstract class BaseDto
{
public string Uid { get; set; }
}
public class RequestDto : BaseDto
{
public string SomeData { get; set; }
}
Im using my ReuqestDto class in my controller actions:
[HttpGet]
public IEnumerable<string> Get(RequestDto req)
{
// some logic on request
if (req.Uid != null)
{
// perform action
}
}
The user passing only SomeData property to me. In my JWT Token i have saved some information about Uid for BaseDto. What is the best way to write data to Uid using middleware/filter to have that information in my Get() method? I Tried to serialized HttpContext.Request.Body but not success because i cant find, how to do it properly. Or maybe there are better solutions for this problem? How to write data to my incoming objects in app?
This is probably what you want.
You should to create own interface for models like that
public interface IMyRequestType { }
Your model should implement it for finding model in FilterAttribute
public class MyModel : IMyRequestType
{
public string ID { get; set; }
}
And create your filter attribute with OnActionExecuting implentation
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute() : base(typeof(MyFilterImpl)) { }
private class MyFilterImpl : IActionFilter
{
private readonly ILogger _logger;
public MyFilterAttributeImpl(ILoggerFactory loggerFactory)
{
// get something from DI
_logger = loggerFactory.CreateLogger<MyFilterAttributeImpl>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
// get your request model
var model = context.ActionArguments.Values.OfType<IMyRequestType>().Single();
// get your key
//context.HttpContext.User or whatever
// do something with model
}
public void OnActionExecuted(ActionExecutedContext context)
{
// perform some logic work
}
}
}
I often created a filter which implements Attribute and IAsyncActionFilter to get the information before go inside the Controller's action.
Here is an example,
using System.IdentityModel.Tokens.Jwt;
public class UserProfileFilter : Attribute, IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string uid = string.Empty;
StringValues authHeaderVal = default(StringValues);
// Get UID from JWT
if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out authHeaderVal))
{
string bearerTokenPrefix = "Bearer";
string accessToken = string.Empty;
string authHeaderStr = authHeaderVal.ToString();
if (!string.IsNullOrEmpty(authHeaderStr) && authHeaderStr.StartsWith(bearerTokenPrefix, StringComparison.OrdinalIgnoreCase))
{
accessToken = authHeaderStr.Replace(bearerTokenPrefix, string.Empty, StringComparison.OrdinalIgnoreCase).Trim();
}
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(accessToken);
uid = token.Claims.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value;
}
// Or Get UID from ActionExecutingContext
var user = context.HttpContext.User;
if (user.Identity.IsAuthenticated)
{
uid = user.Claims.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value;
}
// Get payload
RequestDto payload = (RequestDto)context.ActionArguments?.Values.FirstOrDefault(v => v is RequestDto);
payload.Uid = uid;
await next();
}
}
And then you can put the filter on any action.
[HttpPost]
[Authorize]
[TypeFilter(typeof(UserProfileFilter))]
public ActionResult<IActionResult> AdminGet(RequestDto request)
{
Debug.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(request));
return this.Ok();
}
The above filter will use the sub claim's value to overwrite the value of the incoming payload.
For example, if I post the payload as following,
{
"uid" : "",
"someData": "Test"
}
The action will finally output {"Uid":"MyID","SomeData":"Test"}.
I have this class as you can see :
public class mybaseclass
{
public string token = "";
private readonly HttpContextAccessor iHTTP;
public mybaseclass([FromServices]HttpContextAccessor IHTTP)
{
//this.httpContext = httpContext;
iHTTP = IHTTP;
}
public mybaseclass()
{
}
protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
{
// var t = null;
try
{
// iHTTP.HttpContext.Request.Cookies[key]
var t = iHTTP.HttpContext.Request.Cookies["Authorization"];
if (t == null)
{
token = t;
}
}
catch(Exception aaa)
{
}
var msg = new HttpRequestMessage();
// SET THE BEARER AUTH TOKEN
msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
return Task.FromResult(msg);
}
}
And this class is called in this class :
public partial class Default1Client : mybaseclass, IDefault1Client
{
private string _baseUrl = "";
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public Default1Client(string baseUrl)
{
BaseUrl = baseUrl;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
// other part of code
}
My problem is when I call Default1Client,the class calls the mybaseclass constructor that doesn't have any parameters ,but I need the constructor with httpcontextaccessorto be called
Modify your constructor to pass it through:
public Default1Client(string baseUrl, HttpContextAccessor contextAccessor) : base(contextAccessor)
{
//etc....
And populate it when you register:
services.AddScoped<IDefault1Client>(provider => { return new Default1Client
(
"localhost:44381",
provider.GetService(typeof(HttpContextAccessor)) as HttpContextAccesor
)});
In this case you want to specify the parameters, we can inject them using a function that will get the httpContextAccessor from your baseurl:
// you can use a function call to get the accessor
public Default1Client(string baseUrl) : mybaseclass(getHttpContextAccessor(baseUrl))
If you pass it the parameters that will correspond to httpcontextaccessor the constructor which takes parameters will be called.
What I'm trying to do is make an api service using .NET Core where I am making an array from a database .... I'm getting a 'Cannot implicitly convert type 'System.Action[] to SynCore.DbrAction[]'.
Am I going in the right direction? I'm trying to make a api service that my Angular client can consume.
Action Controller
public class ActionsController : ControllerBase
{
private IActionService _actionService;
public ActionsController(IActionService actionService)
{
_actionService = actionService;
}
//GET: api/Actions
[HttpGet]
public IActionResult GetActions(string userId, string sessionId)
{
// TODO: Figure out when recurseOnMenus is true, pass it accordingly instead of forcing false.
DbrAction[] dbrActions = _actionService.GetActions(userId, sessionId);
return Ok(dbrActions);
}
Action Service
public interface IActionService
{
Action[] GetActions(string userId, string sessionId);
}
public class ActionService : IActionService
{
private DataContext _context;
public ActionService(DataContext context)
{
_context = context;
}
public DbrAction[] GetActions(string sessionId)
{
UserStateHelper ush = new UserStateHelper();
UserState us = ush.CreateUserState(sessionId);
XioTable xt = new XioTable(us, T.User);
DbrUser user = (DbrUser)xt.LoadSingleRow(us.UserTag, C.User_DefaultActionTag);
Actions[] actions = {};
List<DbrAction> dbrActions = new List<DbrAction>();
xt = new XioTable(us, T.Action);
xt.SelectCols(C.Action_Description, C.Action_ActionName, C.Action_FolderTag, C.Action_ActionType, C.Action_PrimaryTable);
xt.LoadData();
foreach (DbrAction action in xt.GetAllRows(C.Action_FolderTag))
{
if (!DbrAction.IsTableDeprecated(action.PrimaryTable))
{
if (action.ActionType == ActionType.View || action.ActionType == ActionType.Dashboard)
dbrActions.Add(action);
}
}
us.Completed(LogEntryType.GetStartupProfileData, null);
return dbrActions.ToArray();
}
public Action[] GetActions(string userId, string sessionId)
{
throw new NotImplementedException();
}
I am looking forward to inject RequestContext, per request in .Net Core. inside the service collection.
Someone attempted 8 yrs. ago.
ASP.NET MVC inject per request
public interface IMvcDepency
{
string PathValue { get; set; }
}
public class FakeMvcDepency : IMvcDepency
{
public string PathValue { get; set; }
}
public class MvcDepency : IMvcDepency
{
public string PathValue { get; set; }
public MvcDepency(HttpRequest req)
{
PathValue = req.Path.Value;
}
}
And inject it somewhere in startup, as follows:
services.AddTransient<IMvcDepency, MvcDepency>(x => x.???);
or in OnActionExecuting like below:
public override void OnActionExecuting(ActionExecutingContext actCtx)
{
MvcDepency mvcDepency = actCtx.HttpContext.RequestServices.GetService(typeof(IMvcDepency)) as MvcDepency;
mvcDepency = new MvcDepency(actCtx.HttpContext.Request);
actCtx.HttpContext.RequestServices.AddService(mvcDepency);// AddService method doesn't in exist
}
Current Error:
System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.AspNetCore.Http.HttpRequest' while attempting to activate 'CAWP.Api.Controllers.MvcDepency'.'
Controllers already have access to the HttpRequest object in each of the methods via the base class. But it is only available once a method is called (for obvious reasons!). If you want to wrap it in your own class then you can do it in the OnActionExecuting override.
You can create a new MvcDepency class in OnActionExecuting and reference it in the code. As controllers are created per request you should be able to use a class variable to store the reference.
public class ValuesController : Controller
{
private IMvcDepency _depency;
public ValuesController()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
_depency = new MvcDepency(context.HttpContext.Request);
base.OnActionExecuting(context);
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var path = _depency.PathValue;
return new string[] { "PathValue", path };
}
}
This should result in the MvcDepency class having access to the HttpRequest object.
You should add a factory class for your IMvcDepency interface to avoid the new in OnActionExecuting.