Vaughan Reid's blog

Using middleware to conditionally allow endpoints

There are times when you have controllers or actions that are very useful for debugging issues but you don't really want to expose them in a production environment. One way to achieve this is to take advantage of the separation of the Endpoint Routing Middleware and the Endpoint Middleware components. Another way would be to use a custom authorisation filter but in this case I’ll use middleware.

To recap, the Endpoint Routing Middleware finds the correct endpoint to use and then adds it to the request http context. The Endpoint Middleware then uses the endpoint that was selected to invoke the controller action. This is one of the major changes to routing in asp.net core, as it previously was a single combined component.

An easy way to disable some endpoints based on configuration is to add custom middleware in the middle of the two. This example will change the selected endpoint to a 403 based on if it was going to a marked controller and if the configuration was enabled.

Firstly I created a simple attribute that will mark the controller or action that is only available when debug is enabled.


public class DebugOnlyAttribute : Attribute
{
}

I created a configuration class and bound it in the ConfigureServices method of Startup.cs.


public class DebugOptions
{
    public bool EnableDebugEndpoints { get; set; }
}

public void ConfigureServices(IServiceCollection services)
{

    services.Configure<DebugOptions>(options =>
			Configuration.GetSection(nameof(DebugOptions)).Bind(options));
    //the rest excluded for brevity
}

I add then just add the attribute to my DebugController.


[ApiController]
[DebugOnly]
public class DebugController : ControllerBase
{
    public ActionResult Get()
    {
        Return Ok();
    }
}

Then its just a matter of creating the middleware and registering it in the Startup.Configure method.


public class EnableDebugMiddleware
{
	private RequestDelegate _next;
	private readonly DebugOptions _debugConfiguration;

	public EnableDebugMiddleware(RequestDelegate next, 
	IOptions<DebugOptions> option)
	{
		_next = next;
		_debugConfiguration = option.Value;
	}

	public Task Invoke(HttpContext httpContext)
	{
		var isDebugEndpoint = httpContext
									.GetEndpoint()?.Metadata
									.GetMetadata<DebugOnlyAttribute>();

		if(isDebugEndpoint != null && !_debugConfiguration.EnableDebugEndpoints)
		{
			httpContext.SetEndpoint(new Endpoint((context) =>
						{
						context.Response.StatusCode = StatusCodes.Status403Forbidden;
						return Task.CompletedTask;
						},
						EndpointMetadataCollection.Empty , 
						"Debug endpoints are disabled"));
		}

		return _next(httpContext);
	}
}

//Endpoint Routing Middleware
app.UseRouting();

app.UseMiddleware<EnableDebugMiddleware>();

//Endpoint Middleware
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

If you try this you will see that all debug requests will return a 403 when DebugOptions.EnableDebugEndpoints is false.