Working with ASP.NET MVC
This commit is contained in:
parent
5690adac90
commit
ebf1d60bde
|
@ -6,20 +6,21 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
namespace Ooui.AspNetCore
|
namespace Ooui.AspNetCore
|
||||||
{
|
{
|
||||||
public class ElementResult : ActionResult
|
public class ElementResult : ActionResult
|
||||||
{
|
{
|
||||||
|
readonly Element element;
|
||||||
|
|
||||||
public ElementResult (Element element)
|
public ElementResult (Element element)
|
||||||
{
|
{
|
||||||
|
this.element = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteResultAsync (ActionContext context)
|
public override async Task ExecuteResultAsync (ActionContext context)
|
||||||
{
|
{
|
||||||
var path = context.HttpContext.Request.Path;
|
|
||||||
var response = context.HttpContext.Response;
|
var response = context.HttpContext.Response;
|
||||||
|
|
||||||
response.StatusCode = 200;
|
response.StatusCode = 200;
|
||||||
response.ContentType = "text/html";
|
response.ContentType = "text/html; charset=utf-8";
|
||||||
var html = Encoding.UTF8.GetBytes (UI.RenderTemplate (path));
|
var sessionId = WebSocketHandler.BeginSession (context.HttpContext, element);
|
||||||
|
var html = Encoding.UTF8.GetBytes (UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId));
|
||||||
response.ContentLength = html.Length;
|
response.ContentLength = html.Length;
|
||||||
using (var s = response.Body) {
|
using (var s = response.Body) {
|
||||||
await s.WriteAsync (html, 0, html.Length).ConfigureAwait (false);
|
await s.WriteAsync (html, 0, html.Length).ConfigureAwait (false);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ooui\Ooui.csproj" />
|
<ProjectReference Include="..\Ooui\Ooui.csproj" />
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using Ooui.AspNetCore;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class OouiMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static void UseOoui (this IApplicationBuilder app, string jsPath = "/ooui.js", string webSocketPath = "/ooui.ws", TimeSpan? sessionTimeout = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (webSocketPath))
|
||||||
|
throw new ArgumentException ("A path to be used for Ooui web sockets must be specified", nameof (webSocketPath));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace (jsPath))
|
||||||
|
throw new ArgumentException ("A path to be used for Ooui JavaScript must be specified", nameof (jsPath));
|
||||||
|
|
||||||
|
WebSocketHandler.WebSocketPath = webSocketPath;
|
||||||
|
|
||||||
|
if (sessionTimeout.HasValue) {
|
||||||
|
WebSocketHandler.SessionTimeout = sessionTimeout.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var webSocketOptions = new WebSocketOptions () {
|
||||||
|
KeepAliveInterval = WebSocketHandler.SessionTimeout,
|
||||||
|
ReceiveBufferSize = 4 * 1024
|
||||||
|
};
|
||||||
|
app.UseWebSockets (webSocketOptions);
|
||||||
|
|
||||||
|
app.Use (async (context, next) =>
|
||||||
|
{
|
||||||
|
if (context.Request.Path == jsPath) {
|
||||||
|
var response = context.Response;
|
||||||
|
var clientJsBytes = Ooui.UI.ClientJsBytes;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentLength = clientJsBytes.Length;
|
||||||
|
response.ContentType = "application/javascript; charset=utf-8";
|
||||||
|
response.Headers.Add ("Cache-Control", "public, max-age=3600");
|
||||||
|
using (var s = response.Body) {
|
||||||
|
await s.WriteAsync (clientJsBytes, 0, clientJsBytes.Length).ConfigureAwait (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (context.Request.Path == WebSocketHandler.WebSocketPath) {
|
||||||
|
if (context.WebSockets.IsWebSocketRequest) {
|
||||||
|
await WebSocketHandler.HandleWebSocketRequestAsync (context).ConfigureAwait (false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await next ().ConfigureAwait (false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Ooui.AspNetCore
|
||||||
|
{
|
||||||
|
public static class WebSocketHandler
|
||||||
|
{
|
||||||
|
public static string WebSocketPath { get; set; } = "/ooui.ws";
|
||||||
|
|
||||||
|
public static TimeSpan SessionTimeout { get; set; } = TimeSpan.FromMinutes (5);
|
||||||
|
|
||||||
|
static readonly ConcurrentDictionary<string, PendingSession> pendingSessions =
|
||||||
|
new ConcurrentDictionary<string, PendingSession> ();
|
||||||
|
|
||||||
|
public static string BeginSession (HttpContext context, Element element)
|
||||||
|
{
|
||||||
|
var id = Guid.NewGuid ().ToString ("N");
|
||||||
|
|
||||||
|
var s = new PendingSession {
|
||||||
|
Element = element,
|
||||||
|
RequestTimeUtc = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!pendingSessions.TryAdd (id, s)) {
|
||||||
|
throw new Exception ("Failed to schedule pending session");
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task HandleWebSocketRequestAsync (HttpContext context)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Make sure we get a good ID
|
||||||
|
//
|
||||||
|
if (!context.Request.Query.TryGetValue ("id", out var idValues)) {
|
||||||
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = idValues.FirstOrDefault ();
|
||||||
|
if (id == null || id.Length != 32) {
|
||||||
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find the pending session
|
||||||
|
//
|
||||||
|
if (!pendingSessions.TryRemove (id, out var pendingSession)) {
|
||||||
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reject the session if it's old
|
||||||
|
//
|
||||||
|
if ((DateTime.UtcNow - pendingSession.RequestTimeUtc) > SessionTimeout) {
|
||||||
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OK, Run
|
||||||
|
//
|
||||||
|
var token = CancellationToken.None;
|
||||||
|
var webSocket = await context.WebSockets.AcceptWebSocketAsync ("ooui");
|
||||||
|
var session = new Ooui.UI.Session (webSocket, pendingSession.Element, token);
|
||||||
|
await session.RunAsync ().ConfigureAwait (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PendingSession
|
||||||
|
{
|
||||||
|
public Element Element;
|
||||||
|
public DateTime RequestTimeUtc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ooui/UI.cs
12
Ooui/UI.cs
|
@ -27,17 +27,19 @@ namespace Ooui
|
||||||
|
|
||||||
static readonly byte[] clientJsBytes;
|
static readonly byte[] clientJsBytes;
|
||||||
|
|
||||||
|
public static byte[] ClientJsBytes => clientJsBytes;
|
||||||
|
|
||||||
public static string Template { get; set; } = $@"<!DOCTYPE html>
|
public static string Template { get; set; } = $@"<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>@ElementPath</title>
|
<title>@Title</title>
|
||||||
<meta name=""viewport"" content=""width=device-width, initial-scale=1"" />
|
<meta name=""viewport"" content=""width=device-width, initial-scale=1"" />
|
||||||
<style>@Styles</style>
|
<style>@Styles</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id=""ooui-body""></div>
|
<div id=""ooui-body""></div>
|
||||||
<script src=""/ooui.js""></script>
|
<script src=""/ooui.js""></script>
|
||||||
<script>ooui(""@ElementPath"");</script>
|
<script>ooui(""@WebSocketPath"");</script>
|
||||||
</body>
|
</body>
|
||||||
</html>";
|
</html>";
|
||||||
|
|
||||||
|
@ -297,9 +299,9 @@ namespace Ooui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RenderTemplate (string elementPath)
|
public static string RenderTemplate (string webSocketPath)
|
||||||
{
|
{
|
||||||
return Template.Replace ("@ElementPath", elementPath).Replace ("@Styles", rules.ToString ());
|
return Template.Replace ("@WebSocketPath", webSocketPath).Replace ("@Styles", rules.ToString ());
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataHandler : RequestHandler
|
class DataHandler : RequestHandler
|
||||||
|
@ -454,7 +456,7 @@ namespace Ooui
|
||||||
Console.ResetColor ();
|
Console.ResetColor ();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Session
|
public class Session
|
||||||
{
|
{
|
||||||
readonly WebSocket webSocket;
|
readonly WebSocket webSocket;
|
||||||
readonly Element element;
|
readonly Element element;
|
||||||
|
|
|
@ -15,8 +15,8 @@ namespace AspNetCoreMvc.Controllers
|
||||||
{
|
{
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var button = new Button ();
|
var element = new Label { Text = "Hello Oooooui from Controller" };
|
||||||
return new ElementResult (button);
|
return new ElementResult (element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult About()
|
public IActionResult About()
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace AspNetCoreMvc
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
public Startup(IConfiguration configuration)
|
public Startup (IConfiguration configuration)
|
||||||
{
|
{
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
}
|
}
|
||||||
|
@ -19,29 +19,28 @@ namespace AspNetCoreMvc
|
||||||
public IConfiguration Configuration { get; }
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices (IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
||||||
services.AddMvc();
|
services.AddMvc ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure (IApplicationBuilder app, IHostingEnvironment env)
|
||||||
{
|
{
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment ()) {
|
||||||
{
|
app.UseDeveloperExceptionPage ();
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
app.UseExceptionHandler ("/Home/Error");
|
||||||
app.UseExceptionHandler("/Home/Error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles ();
|
||||||
|
|
||||||
app.UseMvc(routes =>
|
app.UseOoui ();
|
||||||
{
|
|
||||||
routes.MapRoute(
|
app.UseMvc (routes => {
|
||||||
|
routes.MapRoute (
|
||||||
name: "default",
|
name: "default",
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
template: "{controller=Home}/{action=Index}/{id?}");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue