Commit 2d5bb1b0 authored by yangxiaodong's avatar yangxiaodong

add dashboard

parent cce8a976
using System; using System;
using DotNetCore.CAP; using DotNetCore.CAP;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
...@@ -33,5 +34,24 @@ namespace Microsoft.AspNetCore.Builder ...@@ -33,5 +34,24 @@ namespace Microsoft.AspNetCore.Builder
bootstrapper.BootstrapAsync(); bootstrapper.BootstrapAsync();
return app; return app;
} }
public static IApplicationBuilder UseCapDashboard(
this IApplicationBuilder app,
string pathMatch = "/cap")
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (pathMatch == null) throw new ArgumentNullException(nameof(pathMatch));
var marker = app.ApplicationServices.GetService<CapMarkerService>();
if (marker == null)
{
throw new InvalidOperationException("Add Cap must be called on the service collection.");
}
app.Map(new PathString(pathMatch), x => x.UseMiddleware<DashboardMiddleware>(storage, options, routes));
return app;
}
} }
} }
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using DotNetCore.CAP.Dashboard;
using Microsoft.AspNetCore.Http;
namespace DotNetCore.CAP
{
public class DashboardMiddleware
{
private readonly DashboardOptions _options;
private readonly RequestDelegate _next;
private readonly IStorage _storage;
private readonly RouteCollection _routes;
public DashboardMiddleware(RequestDelegate next, DashboardOptions options, IStorage storage, RouteCollection routes)
{
if (next == null) throw new ArgumentNullException(nameof(next));
if (storage == null) throw new ArgumentNullException(nameof(storage));
if (options == null) throw new ArgumentNullException(nameof(options));
if (routes == null) throw new ArgumentNullException(nameof(routes));
_next = next;
_options = options;
_storage = storage;
_routes = routes;
}
public Task Invoke(HttpContext httpContext)
{
var context = new CapDashboardContext(_storage, _options, httpContext);
var findResult = _routes.FindDispatcher(httpContext.Request.Path.Value);
if (findResult == null)
{
return _next.Invoke(httpContext);
}
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var filter in _options.Authorization)
{
if (!filter.Authorize(context))
{
var isAuthenticated = httpContext.User?.Identity?.IsAuthenticated;
httpContext.Response.StatusCode = isAuthenticated == true
? (int)HttpStatusCode.Forbidden
: (int)HttpStatusCode.Unauthorized;
return Task.FromResult(0);
}
}
context.UriMatch = findResult.Item2;
return findResult.Item1.Dispatch(context);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
namespace DotNetCore.CAP
{
internal sealed class DashboardOptionsExtension : ICapOptionsExtension
{
private readonly Action<DashboardOptions> _options;
public DashboardOptionsExtension(Action<DashboardOptions> option)
{
_options = option;
}
public void AddServices(IServiceCollection services)
{
var dashboardOptions = new DashboardOptions();
_options?.Invoke(dashboardOptions);
services.AddSingleton(dashboardOptions);
}
}
public static class CapOptionsExtensions
{
/// <summary>
/// Configuration to use kafka in CAP.
/// </summary>
/// <param name="options">Provides programmatic configuration for the kafka .</param>
/// <returns></returns>
public static CapOptions UseDashboard(this CapOptions capOptions, Action<DashboardOptions> options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
capOptions.RegisterExtension(new DashboardOptionsExtension(options));
return capOptions;
}
}
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace DotNetCore.CAP.Dashboard namespace DotNetCore.CAP.Dashboard
{ {
...@@ -24,4 +25,22 @@ namespace DotNetCore.CAP.Dashboard ...@@ -24,4 +25,22 @@ namespace DotNetCore.CAP.Dashboard
public DashboardRequest Request { get; protected set; } public DashboardRequest Request { get; protected set; }
public DashboardResponse Response { get; protected set; } public DashboardResponse Response { get; protected set; }
} }
public sealed class CapDashboardContext : DashboardContext
{
public CapDashboardContext(
IStorage storage,
DashboardOptions options,
HttpContext httpContext)
: base(storage, options)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
HttpContext = httpContext;
Request = new CapDashboardRequest(httpContext);
Response = new CapDashboardResponse(httpContext);
}
public HttpContext HttpContext { get; }
}
} }
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace DotNetCore.CAP.Dashboard namespace DotNetCore.CAP.Dashboard
{ {
...@@ -15,4 +17,28 @@ namespace DotNetCore.CAP.Dashboard ...@@ -15,4 +17,28 @@ namespace DotNetCore.CAP.Dashboard
public abstract string GetQuery(string key); public abstract string GetQuery(string key);
public abstract Task<IList<string>> GetFormValuesAsync(string key); public abstract Task<IList<string>> GetFormValuesAsync(string key);
} }
internal sealed class CapDashboardRequest : DashboardRequest
{
private readonly HttpContext _context;
public CapDashboardRequest(HttpContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
_context = context;
}
public override string Method => _context.Request.Method;
public override string Path => _context.Request.Path.Value;
public override string PathBase => _context.Request.PathBase.Value;
public override string LocalIpAddress => _context.Connection.LocalIpAddress.ToString();
public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.ToString();
public override string GetQuery(string key) => _context.Request.Query[key];
public override async Task<IList<string>> GetFormValuesAsync(string key)
{
var form = await _context.Request.ReadFormAsync();
return form[key];
}
}
} }
\ No newline at end of file
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace DotNetCore.CAP.Dashboard namespace DotNetCore.CAP.Dashboard
{ {
...@@ -14,4 +16,39 @@ namespace DotNetCore.CAP.Dashboard ...@@ -14,4 +16,39 @@ namespace DotNetCore.CAP.Dashboard
public abstract void SetExpire(DateTimeOffset? value); public abstract void SetExpire(DateTimeOffset? value);
public abstract Task WriteAsync(string text); public abstract Task WriteAsync(string text);
} }
internal sealed class CapDashboardResponse : DashboardResponse
{
private readonly HttpContext _context;
public CapDashboardResponse( HttpContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
_context = context;
}
public override string ContentType
{
get { return _context.Response.ContentType; }
set { _context.Response.ContentType = value; }
}
public override int StatusCode
{
get { return _context.Response.StatusCode; }
set { _context.Response.StatusCode = value; }
}
public override Stream Body => _context.Response.Body;
public override Task WriteAsync(string text)
{
return _context.Response.WriteAsync(text);
}
public override void SetExpire(DateTimeOffset? value)
{
_context.Response.Headers["Expires"] = value?.ToString("r", CultureInfo.InvariantCulture);
}
}
} }
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace DotNetCore.CAP.Dashboard
{
public class RouteCollection
{
private readonly List<Tuple<string, IDashboardDispatcher>> _dispatchers
= new List<Tuple<string, IDashboardDispatcher>>();
public void Add(string pathTemplate, IDashboardDispatcher dispatcher)
{
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));
if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher));
_dispatchers.Add(new Tuple<string, IDashboardDispatcher>(pathTemplate, dispatcher));
}
public Tuple<IDashboardDispatcher, Match> FindDispatcher(string path)
{
if (path.Length == 0) path = "/";
foreach (var dispatcher in _dispatchers)
{
var pattern = dispatcher.Item1;
if (!pattern.StartsWith("^", StringComparison.OrdinalIgnoreCase))
pattern = "^" + pattern;
if (!pattern.EndsWith("$", StringComparison.OrdinalIgnoreCase))
pattern += "$";
var match = Regex.Match(
path,
pattern,
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (match.Success)
{
return new Tuple<IDashboardDispatcher, Match>(dispatcher.Item2, match);
}
}
return null;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment