I'm trying to register my scoped consume filter:
serviceCollectionConfigurator.UsingAzureServiceBus((context, cfg) =>
{
//some code...
cfg.UseConsumeFilter(typeof(MyFilter<>), context);
});
But then in runtime I get NullReferenceException in MassTransit.dll when a message should be consumed. It doesn't even visit filter's constructor. Everything works fine when I remove this filter registration or when I change the filter and register it as non scoped filter. What can be the reason of my issue?
The filter code:
public class MyFilter<T> : IFilter<ConsumeContext<T>> where T : class
{
public MyFilter()
{
}
public void Probe(ProbeContext context)
{
context.CreateFilterScope("ExternalMessageFilter");
}
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
}
}
Thanks!
Related
Does anyone know how I can mark an argument on ActionDescriptor.Parameters to behave in a similar way the [BindNever] is behaving?
I want to always exclude a specific argument from a specific type without keep decorating it on the Controller.
Essentially I would like to be able to add my injected to my functions somehow how similar to the way its done with CancellationToken
public class TestController : ControllerBase
{
[HttpGet(Name = "Get")]
public IActionResult Get([BindNever] IInjectedInterface injected)
{
//Injected can be used in this method
return Ok();
}
[HttpPost(Name = "Post")]
public IActionResult Post([BindNever] IInjectedInterface injected, FormModel formModel)
{
//Injected doesn't work here. There is an error that
/*System.InvalidOperationException: 'Action 'WebApplication3.Controllers.TestController.Post (WebApplication3)'
has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body.
Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route,
and 'FromBodyAttribute' for parameters to be bound from body:
IInjectedInterface injected
FormModel formModel'
*/
return Ok();
}
}
public class ActionExecutionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var injectedParam = context.ActionDescriptor.Parameters.SingleOrDefault(x => x.ParameterType == typeof(IInjectedInterface));
if (injectedParam != null)
{
context.ActionArguments[injectedParam.Name] = new Injected(99);
}
await next.Invoke();
}
private class Injected : IInjectedInterface
{
public Injected(int someData)
{
SomeData = someData;
}
public int SomeData { get; }
}
}
I was able to solve it. Apparently you need to add the following lines on your program.cs to avoid the model binder related errors.
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(IInjectedInterface)));
options.ModelMetadataDetailsProviders.Add(
new BindingSourceMetadataProvider(typeof(IInjectedInterface), BindingSource.Special));
Please have a look at this url first https://weblogs.asp.net/imranbaloch/claims-transformation-and-authorization-policy-in-aspnet5-mvc6
public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity.IsAuthenticated)
{
// get this from cache or db
var country = "Pakistan";
(principal.Identity as ClaimsIdentity).AddClaim(new Claim("Nationality", country));
}
return Task.FromResult(principal);
}
when TransformAsync() will be called.......how to use it ?
[Authorize(Policy = "MustBePakistani")]
public IActionResult Message()
{
return Content("Hi Pakistani");
}
when Message action will be called then how asp.net mvc system will be able to understand what is user's nationality.....is it pakistani or indian ?
guide me how does it work. thanks
In addition to above two segments of the code in your question, you still need to add policy to authorization services in Startup.cs.
Example,
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.AddPolicy("MustBePakistani", policy =>
policy.RequireClaim("Nationality", "Pakistan"));
});
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();
...
}
private class ClaimsTransformer : IClaimsTransformation
{
// Can consume services from DI as needed, including scoped DbContexts
public ClaimsTransformer(IHttpContextAccessor httpAccessor)
{
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity.IsAuthenticated)
{
// get this from cache or db
var country = "Pakistan";
(principal.Identity as ClaimsIdentity)
.AddClaim(new Claim("Nationality", country));
}
return Task.FromResult(principal);
}
}
}
My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}
How do I inject the database to my TestAttribute class as .net core does magically to my controller.
services.AddScoped<DbContextOptions>(p => p.GetRequiredService<DbContextOptions<Context>>());
services.TryAdd(new ServiceDescriptor(typeof(Context), typeof(Context), ServiceLifetime.Transient));
services.AddMvc(options =>
{
options.Filters.Add(new TestAttribute(/*need parameter*/));
}
public HouseController([FromServices] Context database)
{
this.Database = database;
}
public class TestAttribute : ActionFilterAttribute
{
public TestAttribute([FromServices] Context database)
{
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
To inject dependencies into Filters you need to use the ServiceFilter attribute (see docs)
Manually injecting it to an controller action
[ServiceFilter(typeof(TestAttribute))]
public Task<IActionResult> Index()
{
...
}
Also your registration is wrong in the startup for all actions! Filters.Add() accepts a Type, when you want to use attributes which require dependencies (because you can't instantiate them in code), see "Filter Scopes" in the docs.
services.AddMvc(options =>
{
options.Filters.Add(typeof(TestAttribute));
}
My current setup is JBoss Seam 2.2 on JBoss 4.2.3.GA.
I have two Beans like so:
#Name("mailingManager")
#Scope(ScopeType.PAGE)
public class MailingMgr {
private Mailing selectedMailing;
#Observer("mailing.letter.success")
public void recordSuccess(final Object arg) {
if (null != selectedMailing) { // store arg }
}
public void send() {
selectedMailing = new Mailing();
if ('EMAIL' == determineType()) {
EmailSender mailer = (EmailSender) Component.getInstance(EmailSender.class);
mailer.send(getAddresses());
}
// ... more options
}
}
#Name("emailSender")
#Scope(ScopeType.PAGE)
public class EmailSender {
public void send(final Set<String> addresses) {
for (String addr : addresses) {
// ... create a mail
Events.instance().raiseEvent("mailing.letter.success", getGeneratedMail());
}
}
}
The problem is that when recordSuccess() is called selectedMailing is always null.
As a workaround I'm setting selectedMailing in the conversation context manually before calling any code that could potentially trigger my events, and then annotate my field with #In(required=false) to inject it again before recordSuccess is called. But is there a more elegant solution (keeping the decoupling intact)? And why isn't the calling bean reused to handle the event?