How RoutePattern is added to an Endpoint object - c#

Below is some simple code
public class Startup {
public void ConfigureServices(IServiceCollection services) {
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseDeveloperExceptionPage();
app.UseRouting();
app.Use(async (context, next) => {
var endpoint = context.GetEndpoint(); // <--------I put a debugger here
await next();
});
app.UseEndpoints(endpoints => {
endpoints.MapGet("routing", async context => {
await context.Response.WriteAsync("Request Was Routed");
});
});
app.Use(async (context, next) => {
await context.Response.WriteAsync("Terminal Middleware Reached");
});
}
}
When the breakpoint is hit, I can see some properties has been added to the Endpoint instance, for example, RoutePattern property
But Endpoint doesn't have a RoutePattern property:
public class Endpoint {
public Endpoint(RequestDelegate requestDelegate, EndpointMetadataCollection metadata, string displayName);
public string DisplayName { get; }
public EndpointMetadataCollection Metadata { get; }
public RequestDelegate RequestDelegate { get; }
public override string ToString();
Then how does RoutePattern property is added to the Endpoint instance?
Another question is, when I run the application, the breakpoint always get hit twice, not once, why is that, because there is only one request, the breakpoint should only hit once?

Endpoint class really doesn't have RoutePattern property, but it's child class RouteEndpoint has this property (Microsoft.AspNetCore.Routing.RouteEndpoint), when you watch it when it's debugger, you can see it.
And why hit it twice, I'm afraid there's some other code in your app leads it, in my newly created asp.net core MVC app, when the app starts, it only hit it once, but in my signalr mvc project, it hit twice because of the connection of signalr. So I think you need to check your code.
By the way, this document about middleware execution order may help.

Related

C# Polymorphic models with NSwag

Within my Asp.Net Core v5 application we have the following models
public class StorageRecordTypeMetadataBase
{
public string PropertyName { get; set; }
public bool Required { get; set; }
}
public class StringRecordTypeMetadata: StorageRecordTypeMetadataBase
{
public string? ValidationRegex { get; set; }
}
public class NumericRecordTypeMetadata : StorageRecordTypeMetadataBase
{
public int MinValue { get; set; }
public int MaxValue { get; set; }
}
In my application Startup.cs I have registered Swagger and NSwag setup as following:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerDocument();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseOpenApi();
app.UseSwaggerUi3();
}
}
In order to support polymorphism, I have followed the guideline as written by NSwag creator: Guide on inheritance in NSwag, and here is how my model looks like updated:
[JsonConverter(typeof(JsonInheritanceConverter), "discriminator")]
[KnownType(typeof(StringRecordTypeMetadata))]
[KnownType(typeof(NumericRecordTypeMetadata))]
public class StorageRecordTypeMetadataBase
{
public string PropertyName { get; set; }
public bool Required { get; set; }
}
As soon as I run the application, swagger fails as swagger.json could not be generated. Upon investigating the issue I can see the following error message once I try to manually navigate to /swagger/v1/swagger.json
System.MissingMethodException: Method not found: 'System.String
Namotion.Reflection.XmlDocsExtensions.GetXmlDocsSummary(System.Reflection.MemberInfo)'.
at
NSwag.Generation.Processors.OperationSummaryAndDescriptionProcessor.ProcessSummary(OperationProcessorContext
context, List1 attributes) at NSwag.Generation.Processors.OperationSummaryAndDescriptionProcessor.Process(OperationProcessorContext context) at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.RunOperationProcessors(OpenApiDocument document, ApiDescription apiDescription, Type controllerType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription, List1 allOperations, OpenApiDocumentGenerator
swaggerGenerator, OpenApiSchemaResolver schemaResolver) at
NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.AddOperationDescriptionsToDocument(OpenApiDocument
document, Type controllerType, List1 operations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateForControllers(OpenApiDocument document, IGrouping2[] apiGroups, OpenApiSchemaResolver
schemaResolver) at
NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateAsync(ApiDescriptionGroupCollection
apiDescriptionGroups) at
NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateAsync(Object
serviceProvider) at
NSwag.AspNetCore.OpenApiDocumentProvider.GenerateAsync(String
documentName) at
NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GenerateDocumentAsync(HttpContext
context) at
NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GetDocumentAsync(HttpContext
context) at
NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GetDocumentAsync(HttpContext
context) at
NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
I have tried to include the referenced package Namotion.Reflection even myself but that did not help either. Is there anything that I have missed during my configuration?
This was supposed to add discriminator field within the base model so that it would be automatically recognized when I generate my models on front end (React) side. I can achieve this behavior by moving away from NSwag, to Swashbuckle like following:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(c =>
{
c.UseAllOfForInheritance();
c.SelectSubTypesUsing(baseType =>
{
return typeof(StorageRecordType).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType));
});
c.SelectDiscriminatorNameUsing((baseType) => "itemType");
c.SelectDiscriminatorValueUsing((subType) => subType.Name);
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger((SwaggerOptions c) => { });
app.UseSwaggerUI();
}
}
However this completely breaks the NSwag generation process on my React side. Methods from all controllers are put together into a single Client (instead of being separated per controller name), plus some of the classes required in the parameters seem to be gone as well.
How can I fix the NSwag in order to get the discriminator value in my swagger.json response?
Ok so the issue was, that I was using older version of Nswag.AspNetCore. Instead of version 13.10.8 I upgraded to 13.15.5, which works great with package NJsonSchema v 10.6.6

How can I change an endpoint that returns one object to be filterable on which properties to return?

I want to find the cleanest way to do this but haven't had much luck on google. I'm returning an Information object that has properties assemblies, configuration, and cassandra.
I want it to work so if I call /api/Information?filter=Assemblies,Configuration, it returns only the assemblies and configuration properties, leaving out cassandra.
I can do this currently but I'm doing it with if else statements. Is there a better approach to this, maybe with an interface?
I think what you are looking for that would work for .net very well is OData.
You can use odata to do that it is exactly what you need.
Startup.cs should be modified like this.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<yourDbContext>(opt => opt.UseInMemoryDatabase("yourList"));
services.AddControllers().AddOData(opt => opt.AddRouteComponents("api", GetEdmModel())
.Filter().Select());
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Send "~/$odata" to debug routing if enable the following middleware
// app.UseODataRouteDebug();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static IEdmModel GetEdmModel()
{
// …
}
}
Controller should be like this:
public class InformationController : ControllerBase
{
[HttpGet]
[EnableQuery]
public IEnumerable<Information> Get()
{
//…your logic
}
}
Api
So if you do /api/Information?Select=Assemblies,Configuration you will only get assemblies and configuration.

Rewrite Route in middleware after Endpoint got matched

I am currently writing a rate-limiter with the help of a middleware, this is based on an per-user basis. When the rate-limit exceeds for a user, I want to rewrite the route to my 429 page which tells the use that he exceeded the rate-limit.
This is an example of what my current middle-ware looks like:
public class RateLimitMiddleware : IMiddleware
{
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if(RateLimitExceeded(context.User))
{
context.Request.Path = "/Home/Ratelimit";
}
return next.Invoke(context);
}
public bool RateLimitExceeded(ClaimsPrincipal user)
{
//...
}
}
Now to my Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimit(); // This adds the RateLimitMiddleware class to the middlewares.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Now to the actual problem, you can't rewrite the route in any middleware after the UseRouting. Since it is the actual method deciding which Endpoint to pick. Therefore, is it possible to rewrite the route afterwards?
However, please do note, that I don't want to redirect the user to the rate-limit view. Additionally, I can't just move the middleware up in the chain, since I need the HttpContext.User to be set.

Policy based Authorization not working in asp.net core

I'm trying to get Policy based Authorization working in .net core 2.1 based on this article: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1
However I cannot seem to get it to fire.
In the below example, I've commented out the context.suceeed line, so I would have thought my api call to my usercontroller would fail.
However my API call is being accepted.
What am I doing wrong?
This is my startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAuthorizationHandler, VerifyAuthCookieHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("VerifyAuthCookie", policy =>
policy.Requirements.Add(new VerifyAuthCookieRequirement()));
});
services.AddMvcCore().AddJsonFormatters();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
}
Here is my handler
public class VerifyAuthCookieHandler : AuthorizationHandler<VerifyAuthCookieRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
VerifyAuthCookieRequirement requirement)
{
//context.Succeed(requirement);
return Task.CompletedTask;
}
}
And here is my requirement:
public class VerifyAuthCookieRequirement : IAuthorizationRequirement
{
public VerifyAuthCookieRequirement()
{
}
}
And finally, my controller:
[Route("api/[controller]")]
[Authorize(Policy = "VerifyAuthCookie")]
public class UserController : Controller
{
}
If I add code in HandleRequirementAsync and set a breakpoint then it's not being hit when I debug, so my Handler doesn't appear to be called at all.
You should call app.UseAuthentication(); before the app.UseMvc(); in the Configure method of the Startup class. This will add the ASP.NET Core authentication middleware to the request pipeline.
Since you are using services.AddMvcCore() we'll need to configure the authorization services manually, something services.AddMvc() does for you automatically. We should add .AddAuthorization() to the IMvcCoreBuilder.
This will add the default Authentication- and Authorization services and a PolicyEvaluator.
If you're interested in the exact services that will be registered in your DI container you should follow this link.
I had similar issue, I fix it by :
services.AddMvcCore().AddAuthorization().AddJsonFormatters();

Asp.net Core web api middleware Error `The 'Invoke' method's first argument must be of type 'HttpContext'`

I have created following middleware in ASP.NET Core WebAPI project.
SignalrQsHeaderMiddleware.cs
public class SignalrQsHeaderMiddleware
{
private readonly RequestDelegate _next;
public SignalrQsHeaderMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
/*
if (context.Request.Path.Value.Contains("/restaurantHub"))
{
var qsHeader = context.Request.Query["access_token"].ToString();
if (!qsHeader.IsNullOrEmpty())
{
context.Request.Headers.Add("Authorization",qsHeader);
}
}*/
await _next.Invoke(context);
}
}
In Startup.cs added following line.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Other services.
app.UseMiddleware<SignalrQsHeaderMiddleware>();//<-- added here
}
And when i run project following error occur.
The 'Invoke' method's first argument must be of type 'HttpContext'. at
Microsoft.AspNetCore.Builder.<>c__DisplayClass3_0.b__0(RequestDelegate
next) at
Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build() at
Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
Note: I have try this solution Here
But still i am facing the same issue.
Any suggestion what i am doing wrong ?
Your middleware is the last one, so there is no "next" item.
Looks like the bug is that we can see wrong error message.
But you must check, if _next != null anyway.
As I understood, the error message says that the Invoke methods first param should be of type HttpContext . See below code for example. The Invoke method's first param is IApplicationBuilder. This creates problem.
public Task Invoke( IApplicationBuilder app, HttpContext httpContext)
{
app.UseExceptionHandler(error =>
{
error.Run(async context =>
{
await context.Response.WriteAsync("from Exception Handler middleware");
});
});
return _next(httpContext);
}
Changed this to below. My issue got fixed. It should always be HttpContext type as first parameter for Invoke.
public Task Invoke(HttpContext httpContext, IApplicationBuilder app)
{
app.UseExceptionHandler(error =>
{
error.Run(async context =>
{
await context.Response.WriteAsync("from Exception Handler middleware");
});
});
return _next(httpContext);
}
Please check in your custom middleware if your parameter order is placed as above. I can't see anything on the current middleware of the code you shared. I would suggest you to check in other middlewares as well for the order.

Categories

Resources