ASP.NET Core MVC inject per request - c#

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.

Related

Overwrite request object in ASP .NET Core

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"}.

How should I get the Request in ApiController constructor?

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;
}

Overriding BadRequest response in ASP.NET Core ResourceFilter

I am implementing a resource filter to store invalid requests in database and override returned BadRequest response.
I stored invalid requests successfully but I am struggling with overriding response, I tried the following:
public class MyFilter : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
;
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
if (!context.ModelState.IsValid)
{
//store request in data base
context.Result= new BadRequestObjectResult(new MyErrorModel(){ID = "1",FriendlyMessage = "Your request was invalid"});
}
}
}
public class MyErrorModel
{
public string FriendlyMessage { get; set; }
public string ID { get; set; }
}
But the returned response is not being overridden.
Is there a way to override the response inside Resource filters?
P.S: I am using [ApiController] attribute.
As we all kown , the IResourceFilter runs immediately after the authorization filter and is suitable for short-circular .
However , you will make no influence on the result by setting Result=new BadRequestObjectResult() when the result execution has finished .
See the workflow as below :
According to the workflow above , we should run the MyFilter after the stage of model binding and before the stage of result filter . In other words , we should put the logic into a action filter . Since there's already a ActionFilterAttribute out of box , just create a MyFilterAttribute which inherits from the ActionFilterAttribute :
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
//store request in data base
context.Result = new BadRequestObjectResult(new MyErrorModel() { ID = "1", FriendlyMessage = "Your request was invalid" });
}
}
}
Here's a screenshot the filter works :
[Edit]:
The code of controller decorated with [ApiController]:
namespace App.Controllers
{
[ApiController]
[Route("Hello")]
public class HelloController : Controller
{
[MyFilter]
[HttpGet("index")]
public IActionResult Index(int x)
{
var y =ModelState.IsValid;
return View();
}
}
}

Error calling ServiceStack service with mvc4 Controller

I'm trying to figure out how to call a service from an asp mvc4 controller, but I just can't. I already set up the services but when I'm trying to call from my controller method it displays this :
http://s10.postimg.org/5ojcryn7t/error_1.png
Here's my code:
Global.asax.cs
namespace mvc4_servicestack
{
// Nota: para obtener instrucciones sobre cómo habilitar el modo clásico de IIS6 o IIS7,
// visite http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public class AppHost : AppHostBase
{
public AppHost()
: base("Nombre del Host de los WebService", typeof(SS_WebServices.StatusService).Assembly)
{ }
public override void Configure(Funq.Container container)
{
}
}
protected void Application_Start()
{
new AppHost().Init();
AreaRegistration.RegisterAllAreas();
// WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
SS_WebServices.cs
namespace mvc4_servicestack
{
public class SS_WebServices
{
//Request DTO Objet
[ServiceStack.ServiceHost.Route("/Home/About/statuss")]
[ServiceStack.ServiceHost.Route("/Home/About/statuss/{Name}")]
public class StatusQuery : ServiceStack.ServiceHost.IReturn<StatusResponse>
{
public string Name { get; set; }
public int Id { get; set; }
}
//Response DTO Object
public class StatusResponse
{
public string Result { get; set; }
}
//Service
public class StatusService : ServiceStack.ServiceInterface.Service
{
//Implement teh Method VERB (POST,GET,PUT,DELETE) OR Any for ALL VERBS
public object Any(StatusQuery request)
{
//Looks strange when the name is null so we replace with a generic name.
var name = request.Name ?? "John Doe";
return new StatusResponse { Result = "Hello, " + name };
}
}
}
}
I've been reading the post Should ServiceStack be the service layer in an MVC application or should it call the service layer?
But Still I can't understand things like :
WHY Register returns an Interface instead a GreeterResponse? container.Register(c => new Greeter());
I really hope you guys can help me...!
Since ServiceStack is hosted in the same AppDomain as MVC, you don't need to use a ServiceClient and go through HTTP to access ServiceStack Services, instead you can just resolve and call the Service normally, E.g:
public HelloController : ServiceStackController
{
public void Index(string name)
{
using (var svc = base.ResolveService<HelloService>())
{
ViewBag.GreetResult = svc.Get(name).Result;
return View();
}
}
}
Outside of a Controller a ServiceStack Service can be resolved via HostContext.ResolveService<T>()
See the MVC Integration docs for more info.
But Still I can't understand things like : WHY Register returns an Interface instead a GreeterResponse? container.Register(c => new Greeter());
Greeter is just a normal C# dependency (i.e. it's not a ServiceStack Service). The container isn't returning IGreeter here, instead it's saying to "Register the Greeter implemenation against the IGreeter interface":
container.Register<IGreeter>(c => new Greeter());
If you registered it without, it will just be registered against the concrete type, e.g. these are both equivalent:
container.Register(c => new Greeter());
container.Register<Greeter>(c => new Greeter());
Which means you would need to specify the concrete dependency to get it auto-wired, e.g:
public class MyService : Service
{
public Greeter Greeter { get; set; }
}
Instead of being able to use:
public IGreeter Greeter { get; set; }
Which is much easier to test given that it's a mockable interface.

Bind Controller Context with Ninject

I have a service class that is required to create a pdf, which needs the ControllerContext injected, in order to render a the html => pdf.
This service is called through a middle tier, that has no reference to the web/mvc project. which is fine, since ninject does all the required injection of services etc.
This is what the Service looks like (simplified for these purposes)
public class PdfCreatorService : AbstractUrlBasedPdfCreatorService
{
[Inject]
public ControllerContext ControllerContext { get; set; }
public override byte[] CreateReport(int reportId)
{
var result = new PdfController().CreateReport(reportId);
using (var it = new ResponseCapture(ControllerContext.RequestContext))
{
result.ExecuteResult(ControllerContext);
return it.ReadAllContents();
}
}
}
Here is the simplified call stack:
Web.HomeController.SendEmailWithPdf(int id) calls:
MiddleTier.BusinessLogic.SendEmailWithPdf(int id) calls:
Web.Services.PdfCreatorService.CreateReport(int id)
Ninject is reaching PdfCreatorService, no problem, but: I need to somehow forward the ControllerContext from the HomeController (through the middle tier) to the PdfCreatorService.
Whilst the middle tier cannot have any reference to the ControllerContext.
I've looked at Providers, Factories, Resolver, etc.
But couldn't find the right solution.
Any help is appreciated!
Cheers
OK. I've come up with a solution that I'm happy with.
Here is how I'm retrieving the ControllerContext:
public class PdfCreatorService
{
[Inject]
public ControllerContextProvider contextProvider { get; set; }
[Inject]
public PdfController pdfController { get; set; }
public override byte[] CreateReport(int reportId)
{
var context = contextProvider.GetControllerContext();
using (var stream = new ResponseCapture(context.RequestContext))
{
// Setup Controller
var routeData = new RouteData(context.RouteData.Route, context.RouteData.RouteHandler);
routeData.Values.Add("action", "CreateReport");
routeData.Values.Add("controller", "Pdf");
routeData.Values.Add("id", reportId);
var pdfContext = new ControllerContext(context.HttpContext, routeData, pdfController);
// Execute Controller
var result = pdfController.CreateReport(reportId);
result.ExecuteResult(pdfContext);
return stream.ReadAllContents();
}
}
}
This is how to set the context in the original controller:
public abstract class HomeController : Controller
{
[Inject]
public ControllerContextProvider ControllerContextProvider { get; set; }
protected override IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
{
ControllerContextProvider.GetControllerContext = () => ControllerContext;
return base.BeginExecute(requestContext, callback, state);
}
}
This is what the provider class looks like:
public class ControllerContextProvider
{
public Func<ControllerContext> GetControllerContext { get; set; }
}
This is how I'm binding it:
public class PortalNinjectModule : NinjectModule
{
public override void Load()
{
Bind<ControllerContextProvider>().ToSelf().InRequestScope();
}
}
Still interested to see whether anyone has a more elegant solution.
You could create a Custom Controller factory.
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName);
HttpContext.Current.Request["controllerInstance"] = controller;
return controller;
}
}
You need to register this controller in your global.asax:
ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
After that you can configure Ninject to resolve the ControllerContext from the request:
kernel.Bind<ControllerContext>().ToMethod(ctx => ((Controller)HttpContext.Current.Request["controllerInstance"]).ControllerContext);

Categories

Resources