I am looking for a C# URL router component. Something very classic, taking inputs string such as /resources/:id/stuff and calling the appropriate methods. Something similar to the default ASP.net routing or RestfullRouting.
However, I am not using HTTP and I don't want a full HTTP stack/framework. I am looking for something light to route my MQTT messages.
Do you know if such a component exist?
The following non-optimized, not really defensively coded code parses URIs against routes:
public class UriRouteParser
{
private readonly string[] _routes;
public UriRouteParser(IEnumerable<string> routes)
{
_routes = routes.ToArray();
}
public Dictionary<string, string> GetRouteValues(string uri)
{
foreach (var route in _routes)
{
// Build RegEx from route (:foo to named group (?<foo>[a-z0-9]+)).
var routeFormat = new Regex("(:([a-z]+))\\b").Replace(route, "(?<$2>[a-z0-9]+)");
// Match uri parameter to that regex.
var routeRegEx = new Regex(routeFormat);
var match = routeRegEx.Match(uri);
if (!match.Success)
{
continue;
}
// Obtain named groups.
var result = routeRegEx.GetGroupNames().Skip(1) // Skip the "0" group
.Where(g => match.Groups[g].Success && match.Groups[g].Captures.Count > 0)
.ToDictionary(groupName => groupName, groupName => match.Groups[groupName].Value);
return result;
}
// No match found
return null;
}
}
It makes a few assumptions about the input (both routes and URIs), but basically it picks the :foo parameter names from the routes and makes named capture groups from that, which are matched against the input URI.
To be used like this:
var parser = new UriRouteParser(new []{ "/resources/:id/stuff" });
var routeValues = parser.GetRouteValues("/resources/42/stuff");
This will yield a dictionary of { "id" = "42" }, which you can then use as you like.
I quickly changed #CodeCaster's solution to attach and invoke delegates.
public class UriRouter
{
// Delegate with a context object and the route parameters as parameters
public delegate void MethodDelegate(object context, Dictionary<string, string> parameters);
// Internal class storage for route definitions
protected class RouteDefinition
{
public MethodDelegate Method;
public string RoutePath;
public Regex RouteRegEx;
public RouteDefinition(string route, MethodDelegate method)
{
RoutePath = route;
Method = method;
// Build RegEx from route (:foo to named group (?<foo>[a-z0-9]+)).
var routeFormat = new Regex("(:([a-z]+))\\b").Replace(route, "(?<$2>[a-z0-9]+)");
// Build the match uri parameter to that regex.
RouteRegEx = new Regex(routeFormat);
}
}
private readonly List<RouteDefinition> _routes;
public UriRouter()
{
_routes = new List<RouteDefinition>();
}
public void DefineRoute(string route, MethodDelegate method)
{
_routes.Add(new RouteDefinition(route, method));
}
public void Route(string uri, object context)
{
foreach (var route in _routes)
{
// Execute the regex to check whether the uri correspond to the route
var match = route.RouteRegEx.Match(uri);
if (!match.Success)
{
continue;
}
// Obtain named groups.
var result = route.RouteRegEx.GetGroupNames().Skip(1) // Skip the "0" group
.Where(g => match.Groups[g].Success && match.Groups[g].Captures.Count > 0)
.ToDictionary(groupName => groupName, groupName => match.Groups[groupName].Value);
// Invoke the method
route.Method.Invoke(context, result);
// Only the first match is executed
return;
}
// No match found
throw new Exception("No match found");
}
}
That can be used like this:
var router = new UriRouter();
router.DefineRoute("/resources/:id/stuff",
(context, parameters) => Console.WriteLine(parameters["id"] + " - " + context));
router.Route("/resources/42/stuff", "abcd");
That will print 42 - abcd on the standard output.
Related
I am building a ASP.NET Core website, where the user signs in, and is able to save things like the theme of the page, which then triggers some js code:
var data = {
userName: userName,
key: "theme",
value: localStorage.getItem("theme")
}
var pendingRequest;
if (pendingRequest) {
pendingRequest.abort()
pendingRequest = null;
}
pendingRequest = $.post('/Account/setExtension', data, function (data) {
pendingRequest = null;
$('#settingsModal').modal('hide');
});
which calls the controller:
[HttpPost]
public void setExtension(string userName, string key, string value)
{
Dictionary<string, object> addData = new Dictionary<string, object>
{
{key, value }
};
GraphHelper.updateExtension(userName, "com.user.roamingSettings", addData).ConfigureAwait(false);
}
which calls the checks if the extension exists, and then decides to create it, or update the existing extension:
public static async Task<bool> extensionExistsAsync(string userName, string extensionName)
{
try
{
var graphClient = GetAuthenticatedClient();
// if extension doesnt exist
bool extensionFound = false;
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
foreach (var extension in extensions)
{
if (extension.Id == extensionName)
{
extensionFound = true;
}
}
return extensionFound;
}
catch (Exception e)
{
Debug.WriteLine(e.Message.ToString());
throw;
}
}
The problem is, the code just stops running on this line:
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
It doesn't throw or anything. Stepping through it line by line, it returns all the way to the assignment, and the output window is empty when it stops. Why is this? How can I get an extension by name, or get all extensions to see which ones exist, either by graph api, or calls?
When ever you use the below call
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
you will be getting UserExtensionsCollectionPage object which gives the list of extensions of a user.
This page doesn't have Id property, the extension objects that are present in this UserExtensionsCollectionPage object have it. So use the below code to print the id and type of the Extensions.
var extensions = await graphClient.Users["a1ee289f-4cab-4fc3-885f-d4cbefb48895"].Extensions.Request().GetAsync();
foreach(var data in extensions.CurrentPage)
{
Console.WriteLine("Id: " + data.Id + "Type: " + data.ODataType );
}
This will give you the data as below.
I'm comparing two graphs, one from a Turtle file with simple literal objects, the other from a file with explicit datatype IRIs. The graphs are otherwise equal.
Graph A:
<s> <p> "o"
Graph B:
<s> <p> "o"^^xsd:string
According to RDF 1.1 (3.3 Literals), "[s]imple literals are syntactic sugar for abstract syntax literals with the datatype IRI http://www.w3.org/2001/XMLSchema#string". This is reflected in the concrete syntax specifications as well (N-Triples, Turtle, RDF XML).
So I'd expect both my graphs to consists of a single triple with a URI node s subject, a URI node p predicate, and a literal node o with type xsd:string object. Based on this I'd expect there to be no difference between the two.
However this is not the case in practice:
var graphStringA = "<http://example.com/subject> <http://example.com/predicate> \"object\".";
var graphStringB = "<http://example.com/subject> <http://example.com/predicate> \"object\"^^<http://www.w3.org/2001/XMLSchema#string>.";
var graphA = new Graph();
var graphB = new Graph();
StringParser.Parse(graphA, graphStringA);
StringParser.Parse(graphB, graphStringB);
var diff = graphA.Difference(graphB);
There's one added and one removed triple in the difference report. The graphs are different, because the datatypes for the object nodes are different: graphA.Triples.First().Object.Datatype is nothing, while graphB.Triples.First().Object.Datatype is the correct URI.
It appears to me that to modify this behaviour I'd have to either
go all the way down to LiteralNode (and change its assumptions about literal nodes), or
create a new GraphDiff (that takes the default datatype of string literals into account).
A workaround is to remove the "default" datatypes:
private static void RemoveDefaultDatatype(IGraph g)
{
var triplesWithDefaultDatatype =
from triple in g.Triples
where triple.Object is ILiteralNode
let literal = triple.Object as ILiteralNode
where literal.DataType != null
where literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#string" || literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#langString"
select triple;
var triplesWithNoDatatype =
from triple in triplesWithDefaultDatatype
let literal = triple.Object as ILiteralNode
select new Triple(
triple.Subject,
triple.Predicate,
g.CreateLiteralNode(
literal.Value,
literal.Language));
g.Assert(triplesWithNoDatatype.ToArray());
g.Retract(triplesWithDefaultDatatype);
}
Is there a way in dotnetrdf to compare simple literals to typed literals in a way that's consistent with RDF 1.1, without resorting to major rewrite or workaround as above?
dotNetRDF is not RDF 1.1 compliant nor do we claim to be. There is a branch which is rewritten to be compliant but it is not remotely production ready.
Assuming that you control the parsing process you can customise the handling of incoming data using the RDF Handlers API. You can then strip the implicit xsd:string type off literals as they come into the system by overriding the HandleTriple(Triple t) method as desired.
Using the Handlers API as per RobV's answer:
class StripStringHandler : BaseRdfHandler, IWrappingRdfHandler
{
protected override bool HandleTripleInternal(Triple t)
{
if (t.Object is ILiteralNode)
{
var literal = t.Object as ILiteralNode;
if (literal.DataType != null && (literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#string" || literal.DataType.AbsoluteUri == "http://www.w3.org/2001/XMLSchema#langString"))
{
var simpleLiteral = this.CreateLiteralNode(literal.Value, literal.Language);
t = new Triple(t.Subject, t.Predicate, simpleLiteral);
}
}
return this.handler.HandleTriple(t);
}
private IRdfHandler handler;
public StripStringHandler(IRdfHandler handler) : base(handler)
{
this.handler = handler;
}
public IEnumerable<IRdfHandler> InnerHandlers
{
get
{
return this.handler.AsEnumerable();
}
}
protected override void StartRdfInternal()
{
this.handler.StartRdf();
}
protected override void EndRdfInternal(bool ok)
{
this.handler.EndRdf(ok);
}
protected override bool HandleBaseUriInternal(Uri baseUri)
{
return this.handler.HandleBaseUri(baseUri);
}
protected override bool HandleNamespaceInternal(string prefix, Uri namespaceUri)
{
return this.handler.HandleNamespace(prefix, namespaceUri);
}
public override bool AcceptsAll
{
get
{
return this.handler.AcceptsAll;
}
}
}
Usage:
class Program
{
static void Main()
{
var graphA = Load("<http://example.com/subject> <http://example.com/predicate> \"object\".");
var graphB = Load("<http://example.com/subject> <http://example.com/predicate> \"object\"^^<http://www.w3.org/2001/XMLSchema#string>.");
var diff = graphA.Difference(graphB);
Debug.Assert(diff.AreEqual);
}
private static IGraph Load(string source)
{
var result = new Graph();
var graphHandler = new GraphHandler(result);
var strippingHandler = new StripStringHandler(graphHandler);
var parser = new TurtleParser();
using (var reader = new StringReader(source))
{
parser.Load(strippingHandler, reader);
}
return result;
}
}
With ASP.NET WebApi, when I send GET api/question?page=0&name=qwerty&other=params and API should give result within pagination envelop.
For that, I'd like to put result and change given page querystring to other values.
I tried as below code but I got a bad feeling about this.
protected HttpResponseMessage CreateResponse(HttpStatusCode httpStatusCode, IEnumerable<Question> entityToEmbed)
// get QueryString and modify page property
var dic = new HttpRouteValueDictionary(Request.GetQueryNameValuePairs());
if (dic.ContainsKey("page"))
dic["page"] = (page + 1).ToString();
else
dic.Add("page", (page + 1).ToString());
var urlHelper = new UrlHelper(Request);
var nextLink= page > 0 ? urlHelper.Link("DefaultApi", dic) : null;
// put it in the envelope
var pageEnvelope = new PageEnvelope<Question>
{
NextPageLink = nextLink,
Results = entityToEmbed
};
HttpResponseMessage response = Request.CreateResponse<PageEnvelope<Question>>(httpStatusCode, pageEnvelope, this.Configuration.Formatters.JsonFormatter);
return response;
}
The NextPageLink gives a lot complex result.:
http://localhost/api/Question?Length=1&LongLength=1&Rank=1&SyncRoot=System.Collections.Generic.KeyValuePair%602%5BSystem.String%2CSystem.String%5D%5B%5D&IsReadOnly=False&IsFixedSize=True&IsSynchronized=False&page=1
My question is,
My page handling with Dictionary approach seems dirty and wrong. Is there better way to address my problem?
I don't know why urlHelper.Link(routeName, dic) gives such a verbose ToString result. How to get rid of unusable Dictionary-related properties?
The key issue probably in your code is the conversion to the HttpRouteValueDictionary. New it up instead and add in a loop all key value pairs.
The approach can be simplified quite a lot, and you should also probably want to consider using an HttpActionResult (so that you can more easily test your actions.
You should also avoid using the httproutevaluedictionary and instead write your UrlHelper like
urlHelper.Link("DefaultApi", new { page = pageNo }) // assuming you just want the page no, or go with the copying approach otherwise.
Where just pre calculate your page no (and avoid ToString);
Write it all in an IHttpActionResult that exposes an int property with the page No. so you can easily test the action result independently of how you figure out the pagination.
So something like:
public class QuestionsResult : IHttpActionResult
{
public QuestionResult(IEnumerable<Question> questions>, int? nextPage)
{
/// set the properties here
}
public IEnumerable<Question> Questions { get; private set; }
public int? NextPage { get; private set; }
/// ... execution goes here
}
To just get the page no, do something like:
Source - http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21
string page = request.Uri.ParseQueryString()["page"];
or
you can use this extension method below (from Rick Strahl)
public static string GetQueryString(this HttpRequestMessage request, string key)
{
// IEnumerable<KeyValuePair<string,string>> - right!
var queryStrings = request.GetQueryNameValuePairs();
if (queryStrings == null)
return null;
var match = queryStrings.FirstOrDefault(kv => string.Compare(kv.Key, key, true) == 0);
if (string.IsNullOrEmpty(match.Value))
return null;
return match.Value;
}
I'm still a beginner for .NET, C#, RESTful API. Now, I'm learning the POST method in RESTful API. Here is the sample coding of POST method, but I still can't get the meaning of the coding. Can anyone provide me an explanation for each line of the coding in a clearly understand way? Or if you don't mind, can you explain it to me by using comment style? For example:
public string message; //to declare message for what use...`
public class MessageController : ApiController
{
// GET api/values
public class MessageToShow
{
public string message;
public string from;
}
public List<MessageToShow> Get()
{
var x = new cheeAdDCClf3rfFREntities();
var y=x.messages.Take(100);
List<MessageToShow> messageToShow = new List<MessageToShow>();
foreach (var xx in y)
{
MessageToShow m = new MessageToShow();
member me = x.members.FirstOrDefault(j => j.ID == xx.from);
if (me != null)
{
m.from = me.username;
m.message = xx.message1;
messageToShow.Add(m);
}
}
return messageToShow;
}
// POST api/values
public void Post(int memberid, dynamic value)
{
var x = new cheeAdDCClf3rfFREntities();
message m = new message();
m.ID = x.messages.Max(record => record.ID) + 1;
m.from = memberid;
m.message1 = value.value;
x.messages.Add(m);
x.SaveChanges();
}
}
}
I would be very appreciate if anyone would like to share me your knowledge on programming. Thank you so much!!! ^_^
It looks like you posted code for both a GET and POST method.
When I make a GET to your API:
public List<MessageToShow> Get()
{
// Looks like EntityFramework? Represents the items already in database
var x = new cheeAdDCClf3rfFREntities();
// Take to top 100 item already in the database
var y=x.messages.Take(100);
// Create a new list to hold the messages we will return
List<MessageToShow> messageToShow = new List<MessageToShow>();
// For each message in the 100 we just took
foreach (var xx in y)
{
MessageToShow m = new MessageToShow();
// Get the details of the member that send this message
member me = x.members.FirstOrDefault(j => j.ID == xx.from);
// If we found the member, create a message to show
// (populating the message and the username of the member
// who sent it)
if (me != null)
{
m.from = me.username;
m.message = xx.message1;
messageToShow.Add(m);
}
}
// Return the list of messages we just created to the caller of the API
return messageToShow;
}
When I POST to your API this is what happens:
public void Post(int memberid, dynamic value)
{
// Gets the items already in the database
var x = new cheeAdDCClf3rfFREntities();
// Create a new message object
message m = new message();
// Find the highest ID already in the database, then add 1. This is the
// ID for our new item
m.ID = x.messages.Max(record => record.ID) + 1;
// The 'from' property is set to the memberId that the user passed in the POST
m.from = memberid;
// The 'message' property is set to whatever dynamic value is passed in the POST
m.message1 = value.value;
// Add the message to the database
x.messages.Add(m);
x.SaveChanges();
}
To understand more about REST, you can read this:
A beginners Guide To HTTP and REST
The code you posted actually looks more like EntityFramework, which is a way of interacting with a database. It's not specific to APIs.
You can find our more about EF here:
EntityFramework
Suppose I have a set of URIs that I am monitoring for availability. Each URI is either "up" or "down", and new URIs to monitor may be added to the system at any time:
public enum ConnectionStatus
{
Up,
Down
}
public class WebsiteStatus
{
public string Uri
{
get;
set;
}
public ConnectionStatus Status
{
get;
set;
}
}
public class Program
{
static void Main(string[] args)
{
var statusStream = new Subject<WebsiteStatus>();
Test(statusStream);
Console.WriteLine("Done");
Console.ReadKey();
}
private static void Test(IObservable<WebsiteStatus> statusStream)
{
}
}
Now suppose in Test() I want to reactively ascertain:
whether all URIs are down (as a bool)
which URIs are down (as IEnumerable<string>)
So Test would end up creating an observable like IObservable<Tuple<bool, IEnumerable<string>>> where the bool indicates whether all URIs are down and the IEnumerable<string> contains those URIs that are.
How do I go about this? My initial thinking is that I would need to group by the URI, then combine the latest from each group into a list that I could then perform a Select against. However, this did not work out due to the way CombineLatest works.
EDIT: Thanks to Matthew's answer I looked into rxx and found that it implemented a CombineLatest overload in exactly the fashion I would have expected in rx out of the box, except that I needed to change it so that it publishes even when there is only a single source stream being combined (by default it was waiting for a minimum of two source streams). Also, I can't justify pulling in an extra 2MB of binaries for the sake of one method, so I have copy/pasted it into my project. Doing so, I was able to solve as follows:
private static void Test(IObservable<WebsiteStatus> statusStream)
{
statusStream
.GroupBy(x => x.Uri)
.CombineLatest()
.Select(
x =>
{
var down = x.Where(y => y.Status == ConnectionStatus.Down);
var downCount = down.Count();
var downUris = down.Select(y => y.Uri).ToList();
return new
{
AllDown = x.Count == downCount,
DownUris = downUris
};
})
.Subscribe(x =>
{
Console.WriteLine(" Sources down ({0}): {1}", x.AllDown ? "that's all of them" : "some are still up", x.DownUris.Aggregate("", (y, z) => y += (z + " | ")));
});
}
The neatest way is to use the Rxx extension in this answer. An alternative is below, it just keeps a list of sites that are down/up.
var downStream = statusStream
.Aggregate<WebsiteStatus, IEnumerable<string>>(new string[0], (down, newStatus) =>
{
if (newStatus.IsUp)
return down.Where(uri => uri != newStatus.Uri);
else if (!down.Contains(newStatus.Uri))
return down.Concat(new string[] { newStatus.Uri });
else
return down;
});
var upStream = statusStream
.Aggregate<WebsiteStatus, IEnumerable<string>>(new string[0], (up, newStatus) =>
{
if (!newStatus.IsUp)
return up.Where(uri => uri != newStatus.Uri);
else if (!up.Contains(newStatus.Uri))
return down.Concat(new string[] { newStatus.Uri });
else
return up;
});
var allDown = upStream.Select(up => !up.Any());