REST updates.
Rest.cs got a lot of commenting at last. Redirects and upgrades can now be easily added for REST routes via the `Rest.RegisterRedirect(base, target, upgrade)` method. Redirects added for all routes. Upgrades added for `/world/bloodmoon` and `/v2/world/autosave`, as they both use old-style REST verbs.
This commit is contained in:
parent
9d4ced58b9
commit
e3a8112b5b
4 changed files with 346 additions and 31 deletions
|
|
@ -35,18 +35,44 @@ namespace Rests
|
|||
/// <summary>
|
||||
/// Rest command delegate
|
||||
/// </summary>
|
||||
/// <param name="args">RestRequestArgs object containing Verbs, Parameters, Request, and TokenData</param>
|
||||
/// <param name="args"><see cref="RestRequestArgs"/> object containing Verbs, Parameters, Request, and TokenData</param>
|
||||
/// <returns>Response object or null to not handle request</returns>
|
||||
public delegate object RestCommandD(RestRequestArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Describes the data contained in a REST request
|
||||
/// </summary>
|
||||
public class RestRequestArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Verbs sent in the request
|
||||
/// </summary>
|
||||
public RestVerbs Verbs { get; private set; }
|
||||
/// <summary>
|
||||
/// Parameters sent in the request
|
||||
/// </summary>
|
||||
public IParameterCollection Parameters { get; private set; }
|
||||
/// <summary>
|
||||
/// The HTTP request
|
||||
/// </summary>
|
||||
public IRequest Request { get; private set; }
|
||||
/// <summary>
|
||||
/// Token data used by the request
|
||||
/// </summary>
|
||||
public SecureRest.TokenData TokenData { get; private set; }
|
||||
/// <summary>
|
||||
/// <see cref="IHttpContext"/> used by the request
|
||||
/// </summary>
|
||||
public IHttpContext Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="RestRequestArgs"/> with the given verbs, parameters, request, and context.
|
||||
/// No token data is used
|
||||
/// </summary>
|
||||
/// <param name="verbs">Verbs used in the request</param>
|
||||
/// <param name="param">Parameters used in the request</param>
|
||||
/// <param name="request">The HTTP request</param>
|
||||
/// <param name="context">The HTTP context</param>
|
||||
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, IHttpContext context)
|
||||
{
|
||||
Verbs = verbs;
|
||||
|
|
@ -56,6 +82,14 @@ namespace Rests
|
|||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="RestRequestArgs"/> with the given verbs, parameters, request, token data, and context.
|
||||
/// </summary>
|
||||
/// <param name="verbs">Verbs used in the request</param>
|
||||
/// <param name="param">Parameters used in the request</param>
|
||||
/// <param name="request">The HTTP request</param>
|
||||
/// <param name="tokenData">Token data used in the request</param>
|
||||
/// <param name="context">The HTTP context</param>
|
||||
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, SecureRest.TokenData tokenData, IHttpContext context)
|
||||
{
|
||||
Verbs = verbs;
|
||||
|
|
@ -65,16 +99,39 @@ namespace Rests
|
|||
Context = context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A RESTful API service
|
||||
/// </summary>
|
||||
public class Rest : IDisposable
|
||||
{
|
||||
private readonly List<RestCommand> commands = new List<RestCommand>();
|
||||
/// <summary>
|
||||
/// Contains redirect URIs. The key is the base URI. The first item of the tuple is the redirect URI.
|
||||
/// The second item of the tuple is an optional "upgrade" URI which will be added to the REST response.
|
||||
/// </summary>
|
||||
private Dictionary<string, Tuple<string, string>> redirects = new Dictionary<string, Tuple<string, string>>();
|
||||
private HttpListener listener;
|
||||
private StringHeader serverHeader;
|
||||
public Dictionary<string, int> tokenBucket = new Dictionary<string, int>();
|
||||
private Timer tokenBucketTimer;
|
||||
/// <summary>
|
||||
/// Contains tokens used to manage REST authentication
|
||||
/// </summary>
|
||||
public Dictionary<string, int> tokenBucket = new Dictionary<string, int>();
|
||||
/// <summary>
|
||||
/// <see cref="IPAddress"/> the REST service is listening on
|
||||
/// </summary>
|
||||
public IPAddress Ip { get; set; }
|
||||
/// <summary>
|
||||
/// Port the REST service is listening on
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Rest"/> listening on the given IP and port
|
||||
/// </summary>
|
||||
/// <param name="ip"><see cref="IPAddress"/> to listen on</param>
|
||||
/// <param name="port">Port to listen on</param>
|
||||
public Rest(IPAddress ip, int port)
|
||||
{
|
||||
Ip = ip;
|
||||
|
|
@ -83,6 +140,9 @@ namespace Rests
|
|||
serverHeader = new StringHeader("Server", String.Format("{0}/{1}", assembly.Name, assembly.Version));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the RESTful API service
|
||||
/// </summary>
|
||||
public virtual void Start()
|
||||
{
|
||||
try
|
||||
|
|
@ -106,6 +166,11 @@ namespace Rests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the RESTful API service using the given <see cref="IPAddress"/> and port
|
||||
/// </summary>
|
||||
/// <param name="ip"><see cref="IPAddress"/> to listen on</param>
|
||||
/// <param name="port">Port to listen on</param>
|
||||
public void Start(IPAddress ip, int port)
|
||||
{
|
||||
Ip = ip;
|
||||
|
|
@ -113,21 +178,56 @@ namespace Rests
|
|||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the RESTful API service
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
listener.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a command using the given route
|
||||
/// </summary>
|
||||
/// <param name="path">URL route</param>
|
||||
/// <param name="callback">Command callback</param>
|
||||
public void Register(string path, RestCommandD callback)
|
||||
{
|
||||
AddCommand(new RestCommand(path, callback));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="RestCommand"/>
|
||||
/// </summary>
|
||||
/// <param name="com"><see cref="RestCommand"/> to register</param>
|
||||
public void Register(RestCommand com)
|
||||
{
|
||||
AddCommand(com);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a redirection from a given REST route to a target REST route, with an optional upgrade URI
|
||||
/// </summary>
|
||||
/// <param name="baseRoute">The base URI that will be requested</param>
|
||||
/// <param name="targetRoute">The target URI to redirect to from the base URI</param>
|
||||
/// <param name="upgradeRoute">The upgrade route that will be added as an object to the <see cref="RestObject"/> response of the target route</param>
|
||||
/// <param name="parameterized">Whether the route uses parameterized querying or not.</param>
|
||||
public void RegisterRedirect(string baseRoute, string targetRoute, string upgradeRoute = null, bool parameterized = true)
|
||||
{
|
||||
if (redirects.ContainsKey(baseRoute))
|
||||
{
|
||||
redirects.Add(baseRoute, Tuple.Create(targetRoute, upgradeRoute));
|
||||
}
|
||||
else
|
||||
{
|
||||
redirects[baseRoute] = Tuple.Create(targetRoute, upgradeRoute);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="RestCommand"/> to the service's command list
|
||||
/// </summary>
|
||||
/// <param name="com"><see cref="RestCommand"/> to add</param>
|
||||
protected void AddCommand(RestCommand com)
|
||||
{
|
||||
commands.Add(com);
|
||||
|
|
@ -151,11 +251,13 @@ namespace Rests
|
|||
}
|
||||
|
||||
#region Event
|
||||
[Obsolete("This class will be removed in the next release")]
|
||||
public class RestRequestEventArgs : HandledEventArgs
|
||||
{
|
||||
public RequestEventArgs Request { get; set; }
|
||||
}
|
||||
|
||||
[Obsolete("This method will be removed in the next release")]
|
||||
public static HandlerList<RestRequestEventArgs> RestRequestEvent;
|
||||
|
||||
private static bool OnRestRequestCall(RequestEventArgs request)
|
||||
|
|
@ -172,7 +274,11 @@ namespace Rests
|
|||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="HttpListener"/> receives a request
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender of the request</param>
|
||||
/// <param name="e">RequestEventArgs received</param>
|
||||
protected virtual void OnRequest(object sender, RequestEventArgs e)
|
||||
{
|
||||
var obj = ProcessRequest(sender, e);
|
||||
|
|
@ -196,12 +302,25 @@ namespace Rests
|
|||
e.Response.Status = HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process a request received by the <see cref="HttpListener"/>
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender of the request</param>
|
||||
/// <param name="e">RequestEventArgs received</param>
|
||||
/// <returns>A <see cref="RestObject"/> describing the state of the request</returns>
|
||||
protected virtual object ProcessRequest(object sender, RequestEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = e.Request.Uri.AbsolutePath;
|
||||
uri = uri.TrimEnd('/');
|
||||
string upgrade = null;
|
||||
|
||||
if (redirects.ContainsKey(uri))
|
||||
{
|
||||
upgrade = redirects[uri].Item2;
|
||||
uri = redirects[uri].Item1;
|
||||
}
|
||||
|
||||
foreach (var com in commands)
|
||||
{
|
||||
|
|
@ -224,7 +343,17 @@ namespace Rests
|
|||
|
||||
var obj = ExecuteCommand(com, verbs, e.Request.Parameters, e.Request, e.Context);
|
||||
if (obj != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(upgrade) && obj is RestObject)
|
||||
{
|
||||
if (!(obj as RestObject).ContainsKey("upgrade"))
|
||||
{
|
||||
(obj as RestObject).Add("upgrade", upgrade);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
|
@ -242,6 +371,15 @@ namespace Rests
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a <see cref="RestCommand"/> using the provided verbs, parameters, request, and context objects
|
||||
/// </summary>
|
||||
/// <param name="cmd">The REST command to execute</param>
|
||||
/// <param name="verbs">The REST verbs used in the command</param>
|
||||
/// <param name="parms">The REST parameters used in the command</param>
|
||||
/// <param name="request">The HTTP request object associated with the command</param>
|
||||
/// <param name="context">The HTTP context associated with the command</param>
|
||||
/// <returns></returns>
|
||||
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms, IRequest request, IHttpContext context)
|
||||
{
|
||||
object result = cmd.Execute(verbs, parms, request, context);
|
||||
|
|
@ -253,6 +391,14 @@ namespace Rests
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a request URI from the parameters, verbs, and URI template of a <see cref="RestCommand"/>
|
||||
/// </summary>
|
||||
/// <param name="cmd">The REST command to take the URI template from</param>
|
||||
/// <param name="verbs">Verbs used in building the URI string</param>
|
||||
/// <param name="parms">Parameters used in building the URI string</param>
|
||||
/// <param name="includeToken">Whether or not to include a token in the URI</param>
|
||||
/// <returns></returns>
|
||||
protected virtual string BuildRequestUri(
|
||||
RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true
|
||||
) {
|
||||
|
|
@ -276,12 +422,19 @@ namespace Rests
|
|||
|
||||
#region Dispose
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the RESTful API service
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the RESTful API service
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
|
@ -294,6 +447,9 @@ namespace Rests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~Rest()
|
||||
{
|
||||
Dispose(false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue