URL mapping with C# HttpListener

c#, http, url-mapping

Solution

You can get a similar effect without attributes

HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Start();
while (true)
{
    HttpListenerContext ctx = listener.GetContext();
    ThreadPool.QueueUserWorkItem((_) =>
    {
        string methodName = ctx.Request.Url.Segments[1].Replace("/", "");
        string[] strParams = ctx.Request.Url
                                .Segments
                                .Skip(2)
                                .Select(s=>s.Replace("/",""))
                                .ToArray();


        var method = this.GetType().GetMethod(methodName);
        object[] @params = method.GetParameters()
                            .Select((p, i) => Convert.ChangeType(strParams[i], p.ParameterType))
                            .ToArray();

        object ret = method.Invoke(this, @params);
        string retstr = JsonConvert.SerializeObject(ret);
    });

Usage would be:

http://localhost:8080/getPersonHandler/333

if you really want to use Attributes then

HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Start();
while (true)
{
    HttpListenerContext ctx = listener.GetContext();
    ThreadPool.QueueUserWorkItem((_) =>
    {
        string methodName = ctx.Request.Url.Segments[1].Replace("/", "");
        string[] strParams = ctx.Request.Url
                                .Segments
                                .Skip(2)
                                .Select(s=>s.Replace("/",""))
                                .ToArray();

        var method = this.GetType()
                            .GetMethods()
                            .Where(mi => mi.GetCustomAttributes(true).Any(attr => attr is Mapping && ((Mapping)attr).Map == methodName))
                            .First();

        object[] @params = method.GetParameters()
                            .Select((p, i) => Convert.ChangeType(strParams[i], p.ParameterType))
                            .ToArray();

        object ret = method.Invoke(this, @params);
        string retstr = JsonConvert.SerializeObject(ret);
    });
}

Then you can use as `http://localhost:8080/Person/333` and your definitions would be

class Mapping : Attribute
{
    public string Map;
    public Mapping(string s)
    {
        Map = s;
    }
}

[Mapping("Person")]
public void getPersonHandler(int id)
{
    Console.WriteLine("<<<<" + id);
}

Problem

In the code below I am waiting for any call to the 8080 port. ``` public static void Main() { HttpListener listener = new HttpListener(); listener.Prefixes.Add("http://*:8080/"); listener.Start(); while(isRunning) { HttpListenerContext ctx = listener.GetContext(); new Thread(new Worker(ctx).ProcessRequest).Start(); } } ``` Is it possible to map specific URL patterns to different behavior? I want achieve a REST-style server i.e. a call to `localhost:8080/person/1` will launch `getPersonHandler(int)` ``` [Mapping("*:8080/person/$id")] public void getPersonHandler(int id) { // ... } ``` The `Mapping` syntax is just my wishful analogy to JAX-RS libraries that I know. I would like to do the same in C# (desktop C#, not asp).

Original source