Search code examples
c#httpurl-mapping

URL mapping with C# HttpListener


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).


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);
    }