Reputation: 5248
My base Request
class looks like this:
public class GetAllProjectsQuery : QueryBase<ProjectsListModel>
{
}
public abstract class QueryBase<T> : UserContext, IRequest<T> // IRequest is MediatR interface
{
}
public abstract class UserContext
{
public string ApplicationUserId { get; set; } // and other properties
}
I want to write a middleware to my .NET Core 3.1 WebApi
that will grab JWT
from request header amd read ApplicationUserId
from it. I started to code something:
public class UserInformation
{
private readonly RequestDelegate next;
public UserInformation(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var jwt = context.Request.Headers["Authorization"];
// read jwt here
var userContext = (UserContext)context.Request.Body; // i know it wont work
userContext.ApplicationUserId = //whats next? Any ideas?
await this.next(context);
}
}
But to be honest i have no idea how to start so here are my questions:
As you can see, every request will be packed with my UserContext
class and so on. How to cast HttpContext.Request.Body
to my request object and attach ApplicationUserId
to it? Is it possible? I want to acces to user credentials from my JWT from headers and i want to have that information in every request in my API (pass it to controller, then to command etc).
If getting this information from middleware is not the best practice, what is?
EDIT: Mcontroller that using MediatR
:
// base controller:
[ApiController]
[Route("[controller]")]
public abstract class BaseController : ControllerBase
{
private IMediator mediator;
protected IMediator Mediator => this.mediator ?? (this.mediator = HttpContext.RequestServices.GetService<IMediator>());
}
// action in ProjectControlle
[HttpGet]
[Authorize]
public async Task<ActionResult<ProjectsListModel>> GetAllProjects()
{
return Ok(await base.Mediator.Send(new GetAllProjectsQuery()));
}
// query:
public class GetAllProjectsQuery : QueryBase<ProjectsListModel>
{
}
// handler:
public class GetAllProjectsQueryHandler : IRequestHandler<GetAllProjectsQuery, ProjectsListModel>
{
private readonly IProjectRepository projectRepository;
public GetAllProjectsQueryHandler(IProjectRepository projectRepository)
{
this.projectRepository = projectRepository;
}
public async Task<ProjectsListModel> Handle(GetAllProjectsQuery request, CancellationToken cancellationToken)
{
var projects = await this.projectRepository.GetAllProjectsWithTasksAsync();
return new ProjectsListModel
{
List = projects
};
}
}
Upvotes: 0
Views: 2475
Reputation: 10065
You might not need a middleware, but need a model binder:
See: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-3.1
Also see: https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-3.1
public class UserContextModelBinder : IModelBinder
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IModelBinder _defaultModelBinder;
public UserContextModelBinder(
IHttpContextAccessor httpContextAccessor,
IOptions<MvcOptions> mvcOptions,
IHttpRequestStreamReaderFactory streamReaderFactory)
{
_httpContextAccessor = httpContextAccessor;
_defaultModelBinder = new BodyModelBinder(mvcOptions.Value.InputFormatters, streamReaderFactory);
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (!typeof(UserContext).IsAssignableFrom(bindingContext.ModelType))
{
return;
}
await _defaultModelBinder.BindModelAsync(bindingContext);
if (bindingContext.Result.IsModelSet && bindingContext.Result.Model is UserContext)
{
var model = (UserContext)bindingContext.Result.Model;
var httpContext = _httpContextAccessor.HttpContext;
// Read JWT
var jwt = httpContext.Request.Headers["Authorization"];
model.ApplicationUserId = jwt;
bindingContext.Result = ModelBindingResult.Success(model);
}
}
}
Then add model binder to UserContext
class:
[ModelBinder(typeof(UserContextModelBinder))]
public abstract class UserContext
{
public string ApplicationUserId { get; set; }
}
Also add IHttpContextAccessor
to services in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpContextAccessor();
}
Upvotes: 1