diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
index 8aa37a05c..5ac92d7ad 100644
--- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings
+++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
@@ -65,6 +65,7 @@
True
True
True
+ True
True
True
True
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
new file mode 100644
index 000000000..5a8ebcd98
--- /dev/null
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+using EmbedIO;
+using Newtonsoft.Json;
+
+namespace Artemis.Core.Services
+{
+ ///
+ /// Represents a plugin web endpoint receiving an object of type and returning any
+ /// or .
+ /// Note: Both will be deserialized and serialized respectively using JSON.
+ ///
+ public class JsonPluginEndPoint : PluginEndPoint
+ {
+ private readonly Action? _requestHandler;
+ private readonly Func? _responseRequestHandler;
+
+ internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action requestHandler) : base(pluginFeature, name, pluginsModule)
+ {
+ _requestHandler = requestHandler;
+ ThrowOnFail = true;
+ }
+
+ internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func responseRequestHandler) : base(pluginFeature, name, pluginsModule)
+ {
+ _responseRequestHandler = responseRequestHandler;
+ ThrowOnFail = true;
+ }
+
+ ///
+ /// Whether or not the end point should throw an exception if deserializing the received JSON fails.
+ /// If set to malformed JSON is silently ignored; if set to malformed
+ /// JSON throws a .
+ ///
+ public bool ThrowOnFail { get; set; }
+
+ #region Overrides of PluginEndPoint
+
+ ///
+ internal override void ProcessRequest(IHttpContext context)
+ {
+ if (context.Request.HttpVerb != HttpVerbs.Post)
+ throw HttpException.MethodNotAllowed("This end point only accepts POST calls");
+
+ context.Response.ContentType = MimeType.Json;
+
+ using TextReader reader = context.OpenRequestText();
+ object? response = null;
+ try
+ {
+ T deserialized = JsonConvert.DeserializeObject(reader.ReadToEnd());
+
+ if (_requestHandler != null)
+ {
+ _requestHandler(deserialized);
+ return;
+ }
+
+ if (_responseRequestHandler != null)
+ response = _responseRequestHandler(deserialized);
+ else
+ throw new ArtemisCoreException("JSON plugin end point has no request handler");
+ }
+ catch (JsonException)
+ {
+ if (ThrowOnFail)
+ throw;
+ }
+
+ using TextWriter writer = context.OpenResponseText();
+ writer.Write(JsonConvert.SerializeObject(response));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/PluginEndPointRegistration.cs b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
similarity index 79%
rename from src/Artemis.Core/Services/WebServer/PluginEndPointRegistration.cs
rename to src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
index 695bc8991..e2250aa8b 100644
--- a/src/Artemis.Core/Services/WebServer/PluginEndPointRegistration.cs
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
@@ -3,7 +3,7 @@ using EmbedIO;
namespace Artemis.Core.Services
{
- public class PluginEndPoint
+ public abstract class PluginEndPoint
{
private readonly PluginsModule _pluginsModule;
@@ -22,19 +22,16 @@ namespace Artemis.Core.Services
public PluginFeature PluginFeature { get; }
///
- /// Gets the name of the end point
+ /// Gets the name of the end point
///
public string Name { get; }
///
- /// Gets the full URL of the end point
+ /// Gets the full URL of the end point
///
public string Url => $"{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
- internal void ProcessRequest(IHttpContext context)
- {
- throw new NotImplementedException();
- }
+ internal abstract void ProcessRequest(IHttpContext context);
private void OnDisabled(object? sender, EventArgs e)
{
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs
new file mode 100644
index 000000000..49dc63614
--- /dev/null
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs
@@ -0,0 +1,36 @@
+using System;
+using EmbedIO;
+
+namespace Artemis.Core.Services
+{
+ ///
+ /// Represents a plugin web endpoint that handles a raw .
+ ///
+ /// Note: This requires that you reference the EmbedIO
+ /// Nuget package.
+ ///
+ ///
+ public class RawPluginEndPoint : PluginEndPoint
+ {
+ ///
+ internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action requestHandler) : base(pluginFeature, name, pluginsModule)
+ {
+ RequestHandler = requestHandler;
+ }
+
+ ///
+ /// Gets or sets the handler used to handle incoming requests to this endpoint
+ ///
+ public Action RequestHandler { get; }
+
+ #region Overrides of PluginEndPoint
+
+ ///
+ internal override void ProcessRequest(IHttpContext context)
+ {
+ RequestHandler(context);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs
new file mode 100644
index 000000000..20cb823c3
--- /dev/null
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+using EmbedIO;
+
+namespace Artemis.Core.Services
+{
+ ///
+ /// Represents a plugin web endpoint receiving an a and returning a or
+ /// .
+ ///
+ public class StringPluginEndPoint : PluginEndPoint
+ {
+ private readonly Action? _requestHandler;
+ private readonly Func? _responseRequestHandler;
+
+ internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action requestHandler) : base(pluginFeature, name, pluginsModule)
+ {
+ _requestHandler = requestHandler;
+ }
+
+ internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func requestHandler) : base(pluginFeature, name, pluginsModule)
+ {
+ _responseRequestHandler = requestHandler;
+ }
+
+ #region Overrides of PluginEndPoint
+
+ ///
+ internal override void ProcessRequest(IHttpContext context)
+ {
+ if (context.Request.HttpVerb != HttpVerbs.Post)
+ throw HttpException.MethodNotAllowed("This end point only accepts POST calls");
+
+ context.Response.ContentType = MimeType.PlainText;
+
+ using TextReader reader = context.OpenRequestText();
+ string? response;
+ if (_requestHandler != null)
+ {
+ _requestHandler(reader.ReadToEnd());
+ return;
+ }
+
+ else if (_responseRequestHandler != null)
+ response = _responseRequestHandler(reader.ReadToEnd());
+ else
+ throw new ArtemisCoreException("String plugin end point has no request handler");
+
+ using TextWriter writer = context.OpenResponseText();
+ writer.Write(response);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
index 8be29ebfa..223f0b505 100644
--- a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
@@ -15,12 +15,59 @@ namespace Artemis.Core.Services
WebServer? Server { get; }
///
- /// Adds a new endpoint for the given plugin feature
+ /// Adds a new endpoint for the given plugin feature receiving an object of type
+ /// Note: Object will be deserialized using JSON.
+ ///
+ /// The type of object to be received
+ /// The plugin feature the end point is associated with
+ /// The name of the end point, must be unique
+ ///
+ /// The resulting end point
+ JsonPluginEndPoint AddJsonEndPoint(PluginFeature feature, string endPointName, Action requestHandler);
+
+ ///
+ /// Adds a new endpoint for the given plugin feature receiving an object of type and
+ /// returning any .
+ /// Note: Both will be deserialized and serialized respectively using JSON.
+ ///
+ /// The type of object to be received
+ /// The plugin feature the end point is associated with
+ /// The name of the end point, must be unique
+ ///
+ /// The resulting end point
+ JsonPluginEndPoint AddResponsiveJsonEndPoint(PluginFeature feature, string endPointName, Func requestHandler);
+
+ ///
+ /// Adds a new endpoint for the given plugin feature receiving an a .
///
/// The plugin feature the end point is associated with
/// The name of the end point, must be unique
+ ///
/// The resulting end point
- PluginEndPoint AddPluginEndPoint(PluginFeature feature, string endPointName);
+ StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action requestHandler);
+
+ ///
+ /// Adds a new endpoint for the given plugin feature receiving an a and returning a
+ /// or .
+ ///
+ /// The plugin feature the end point is associated with
+ /// The name of the end point, must be unique
+ ///
+ /// The resulting end point
+ StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func requestHandler);
+
+ ///
+ /// Adds a new endpoint for the given plugin feature that handles a raw .
+ ///
+ /// Note: This requires that you reference the EmbedIO
+ /// Nuget package.
+ ///
+ ///
+ /// The plugin feature the end point is associated with
+ /// The name of the end point, must be unique
+ ///
+ /// The resulting end point
+ RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Action requestHandler);
///
/// Removes an existing endpoint
diff --git a/src/Artemis.Core/Services/WebServer/PluginsModule.cs b/src/Artemis.Core/Services/WebServer/PluginsModule.cs
index 6208783df..c19af8add 100644
--- a/src/Artemis.Core/Services/WebServer/PluginsModule.cs
+++ b/src/Artemis.Core/Services/WebServer/PluginsModule.cs
@@ -56,26 +56,23 @@ namespace Artemis.Core.Services
///
protected override async Task OnRequestAsync(IHttpContext context)
{
- // Always stick to JSON
- context.Response.ContentType = MimeType.Json;
-
if (context.Route.SubPath == null)
throw HttpException.NotFound();
-
+
// Split the sub path
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
// Expect a plugin ID and an endpoint
if (pathParts == null || pathParts.Length != 2)
throw HttpException.BadRequest("Path must contain a plugin ID and endpoint and nothing else.");
-
+
// Find a matching plugin
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary? endPoints))
throw HttpException.NotFound($"Found no plugin with ID {pathParts[0]}.");
-
+
// Find a matching endpoint
if (!endPoints.TryGetValue(pathParts[1], out PluginEndPoint? endPoint))
throw HttpException.NotFound($"Found no endpoint called {pathParts[1]} for plugin with ID {pathParts[0]}.");
-
+
// It is up to the registration how the request is eventually handled, it might even set a response here
endPoint.ProcessRequest(context);
diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs
index 07fb5ec73..13d7ed7fb 100644
--- a/src/Artemis.Core/Services/WebServer/WebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs
@@ -4,7 +4,6 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using EmbedIO;
-using EmbedIO.Actions;
using EmbedIO.WebApi;
using Newtonsoft.Json;
using Ninject;
@@ -14,11 +13,11 @@ namespace Artemis.Core.Services
{
internal class WebServerService : IWebServerService, IDisposable
{
+ private readonly List _controllers;
private readonly IKernel _kernel;
private readonly ILogger _logger;
private readonly PluginsModule _pluginModule;
private readonly PluginSetting _webServerPortSetting;
- private readonly List _controllers;
public WebServerService(IKernel kernel, ILogger logger, ISettingsService settingsService)
{
@@ -34,6 +33,27 @@ namespace Artemis.Core.Services
Server.Start();
}
+ #region Event handlers
+
+ private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
+ {
+ Server = CreateWebServer();
+ Server.Start();
+ }
+
+ #endregion
+
+ #region IDisposable
+
+ ///
+ public void Dispose()
+ {
+ Server?.Dispose();
+ _webServerPortSetting.SettingChanged -= WebServerPortSettingOnSettingChanged;
+ }
+
+ #endregion
+
public WebServer? Server { get; private set; }
#region Web server managament
@@ -72,32 +92,54 @@ namespace Artemis.Core.Services
#endregion
- #region Event handlers
-
- private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
- {
- Server = CreateWebServer();
- Server.Start();
- }
-
- #endregion
-
- #region IDisposable
-
- ///
- public void Dispose()
- {
- Server?.Dispose();
- _webServerPortSetting.SettingChanged -= WebServerPortSettingOnSettingChanged;
- }
-
- #endregion
-
#region Plugin endpoint management
- public PluginEndPoint AddPluginEndPoint(PluginFeature feature, string endPointName)
+ public JsonPluginEndPoint AddJsonEndPoint(PluginFeature feature, string endPointName, Action requestHandler)
{
- PluginEndPoint endPoint = new(feature, endPointName, _pluginModule);
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
+ if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
+ JsonPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler);
+ _pluginModule.AddPluginEndPoint(endPoint);
+ return endPoint;
+ }
+
+ public JsonPluginEndPoint AddResponsiveJsonEndPoint(PluginFeature feature, string endPointName, Func requestHandler)
+ {
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
+ if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
+ JsonPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler);
+ _pluginModule.AddPluginEndPoint(endPoint);
+ return endPoint;
+ }
+
+ public StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action requestHandler)
+ {
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
+ if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
+ StringPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler);
+ _pluginModule.AddPluginEndPoint(endPoint);
+ return endPoint;
+ }
+
+ public StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func requestHandler)
+ {
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
+ if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
+ StringPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler);
+ _pluginModule.AddPluginEndPoint(endPoint);
+ return endPoint;
+ }
+
+ public RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Action requestHandler)
+ {
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
+ if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
+ RawPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler);
_pluginModule.AddPluginEndPoint(endPoint);
return endPoint;
}