URL mapping with C# HttpListener

Go To StackoverFlow.com

15

In the code below I'm waiting for any call to the 8080 port.

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 behaviour? 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 own wishful analogy to JAX-RS libraries that I know. I'd like to do the same in C# (desktop C#, not asp)

2012-04-04 18:59
by emesx
Do you really need to reinvent the wheel? Web API in ASP.NET MVC 4 can do this - Sam Axe 2012-04-04 19:35
I need a standalone application - emesx 2012-04-04 19:53
FYI ASP.NET Web API can be self-hosted (no IIS - dcstraw 2012-04-18 21:46
inside an executable - emesx 2012-04-19 11:57
If ASP.NET Web API can self-host inside an executable I'd like to see that! (Not saying it can't, just that I don't know that it can) - Scott Offen 2014-10-29 17:37
Use OWIN to self-host: http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-ap - Ian Mercer 2014-11-07 20:18


17

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);
}
2012-04-04 19:29
by L.B
what is this @param kind of variable name - emesx 2012-04-04 19:55
since params is reserved word in c# I used @paramsL.B 2012-04-04 19:56
I didnt know you can use @ in variable names, thanks. I like this answer. Thank you very much - emesx 2012-04-04 19:57
One last thing: can you also filter by request type (GET,PUT ...) - emesx 2012-04-05 15:12
@elmes ctx.Request.HttpMethodL.B 2012-04-05 16:58


5

If you are working in .NET 4.0 or higher and looking for a pre-existing REST server solution that you can plug into (which it sounds like you are), you might want to check out Grapevine. You can get it using NuGet, and the project wiki has lots of sample code. Plus, it is open source, so if you just wanted to see how it can be accomplished, you can see all the source code there.

You can filter requests by path info (using regular expressions) and request methods (GET, POST, etc.).

I am the project author, and I had a similar need as the one you described. Using resources I found here and elsewhere, I built Grapevine so that I would have a solution in my back pocket whenever I needed it again (DRY).

2014-10-29 17:35
by Scott Offen
wow it's awesome, thanks - Luke 2017-03-20 12:28
Ads