Samples icon indicating copy to clipboard operation
Samples copied to clipboard

Create a sample for a Custom Endpoint

Open hananiel opened this issue 4 years ago • 2 comments

Add a sample that shows how to add a custom endpoint using the management endpoints library.

hananiel avatar May 26 '21 14:05 hananiel

One way to do it would be below. I just noodled in a scratch file but haven't actually tested this.

    public static class TestExtensions
    {
        public static IEndpointConventionBuilder MapCustomActuatorEndpoint(this IEndpointRouteBuilder endpoints, Type typeEndpoint, EndpointCollectionConventionBuilder conventionBuilder = null)
        {
            if (endpoints == null)
            {
                throw new ArgumentNullException(nameof(endpoints));
            }

            var serviceProvider = endpoints.ServiceProvider;

            var options = serviceProvider.GetService<CustomEndpointOptions>() as IEndpointOptions;
            var mgmtOptionsCollection = endpoints.ServiceProvider.GetServices<IManagementOptions>();
            var builder = conventionBuilder ?? new EndpointCollectionConventionBuilder();
            foreach (var mgmtOptions in mgmtOptionsCollection)
            {
                var fullPath = options.GetContextPath(mgmtOptions);

                var pattern = RoutePatternFactory.Parse(fullPath);

                // only add middleware if the route hasn't already been mapped
                if (!endpoints.DataSources.Any(d => d.Endpoints.Any(ep => ((RouteEndpoint)ep).RoutePattern.RawText == pattern.RawText)))
                {
                    var pipeline = endpoints.CreateApplicationBuilder()
                        .UseMiddleware(typeof(CustomEndpointMiddleware), mgmtOptions)
                        .Build();
                    var allowedVerbs = options.AllowedVerbs ?? new List<string> { "Get" };

                    builder.AddConventionBuilder(endpoints.MapMethods(fullPath, allowedVerbs, pipeline));
                }
            }

            return builder;
        }

        public static void AddCustomActuator( this IServiceCollection services, IConfiguration config = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            config ??= services.BuildServiceProvider().GetService<IConfiguration>();
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            services.AddActuatorEndpointMapping<CustomEndpoint>();
        }
    }

    public class CustomEndpointOptions : AbstractEndpointOptions
    {
        private const string MANAGEMENT_INFO_PREFIX = "management:endpoints:custom";

        public CustomEndpointOptions()
            : base()
        {
            Id = "custom";
            RequiredPermissions = Permissions.RESTRICTED;
        }

        public CustomEndpointOptions(IConfiguration config)
            : base(MANAGEMENT_INFO_PREFIX, config)
        {
            if (string.IsNullOrEmpty(Id))
            {
                Id = "custom";
            }
        }
    }

    public class CustomEndpointMiddleware : EndpointMiddleware<string, string>
    {
        private readonly RequestDelegate _next;

        public CustomEndpointMiddleware(RequestDelegate next, CustomEndpoint endpoint, IManagementOptions mgmtOptions, ILogger<CustomEndpointMiddleware> logger = null)
            : base(endpoint, mgmtOptions, logger: logger)
        {
            _next = next;
        }

        public Task Invoke(HttpContext context)
        {
            _logger.LogDebug("Info middleware Invoke({0})", context.Request.Path.Value);

            if (_endpoint.ShouldInvoke(_mgmtOptions, _logger))
            {
                return HandleInfoRequestAsync(context);
            }

            return Task.CompletedTask;
        }

        protected internal Task HandleInfoRequestAsync(HttpContext context)
        {
            var serialInfo = HandleRequest();
            _logger?.LogDebug("Returning: {0}", serialInfo);

            context.HandleContentNegotiation(_logger);
            return context.Response.WriteAsync(serialInfo);
        }
    }

    public class CustomEndpoint : AbstractEndpoint<string, string>
    {
        public CustomEndpoint(IEndpointOptions options)
            : base(options)
        {
        }

        public override string Invoke(string arg)
        {
            return "stuff";
        }
    }

hananiel avatar May 26 '21 14:05 hananiel

Then to use this ...

   ...
  .UseEndpoints(endpoints =>
    {
        endpoints.MapActuatorEndpoint(type).RequireAuthorization("TestAuth");
        endpoints.MapCustomActuatorEndpoint(typeof(CustomEndpoint)).RequireAuthorization("TestAuth");
    }))

hananiel avatar May 26 '21 14:05 hananiel