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
|
||||
{
|
||||
public class ElementResult : ActionResult
|
||||
{
|
||||
{
|
||||
readonly Element element;
|
||||
|
||||
public ElementResult (Element element)
|
||||
{
|
||||
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
public override async Task ExecuteResultAsync (ActionContext context)
|
||||
{
|
||||
var path = context.HttpContext.Request.Path;
|
||||
var response = context.HttpContext.Response;
|
||||
|
||||
response.StatusCode = 200;
|
||||
response.ContentType = "text/html";
|
||||
var html = Encoding.UTF8.GetBytes (UI.RenderTemplate (path));
|
||||
response.ContentType = "text/html; charset=utf-8";
|
||||
var sessionId = WebSocketHandler.BeginSession (context.HttpContext, element);
|
||||
var html = Encoding.UTF8.GetBytes (UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId));
|
||||
response.ContentLength = html.Length;
|
||||
using (var s = response.Body) {
|
||||
await s.WriteAsync (html, 0, html.Length).ConfigureAwait (false);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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;
|
||||
|
||||
public static byte[] ClientJsBytes => clientJsBytes;
|
||||
|
||||
public static string Template { get; set; } = $@"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>@ElementPath</title>
|
||||
<title>@Title</title>
|
||||
<meta name=""viewport"" content=""width=device-width, initial-scale=1"" />
|
||||
<style>@Styles</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id=""ooui-body""></div>
|
||||
<script src=""/ooui.js""></script>
|
||||
<script>ooui(""@ElementPath"");</script>
|
||||
<script>ooui(""@WebSocketPath"");</script>
|
||||
</body>
|
||||
</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
|
||||
|
@ -454,7 +456,7 @@ namespace Ooui
|
|||
Console.ResetColor ();
|
||||
}
|
||||
|
||||
class Session
|
||||
public class Session
|
||||
{
|
||||
readonly WebSocket webSocket;
|
||||
readonly Element element;
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace AspNetCoreMvc.Controllers
|
|||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
var button = new Button ();
|
||||
return new ElementResult (button);
|
||||
var element = new Label { Text = "Hello Oooooui from Controller" };
|
||||
return new ElementResult (element);
|
||||
}
|
||||
|
||||
public IActionResult About()
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace AspNetCoreMvc
|
|||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
public Startup (IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
@ -19,29 +19,28 @@ namespace AspNetCoreMvc
|
|||
public IConfiguration Configuration { get; }
|
||||
|
||||
// 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.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
public void Configure (IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
if (env.IsDevelopment ()) {
|
||||
app.UseDeveloperExceptionPage ();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
else {
|
||||
app.UseExceptionHandler ("/Home/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseStaticFiles ();
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
app.UseOoui ();
|
||||
|
||||
app.UseMvc (routes => {
|
||||
routes.MapRoute (
|
||||
name: "default",
|
||||
template: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue