Подтвердить что ты не робот

URL-сопоставление с С# HttpListener

В приведенном ниже коде я жду любого вызова порта 8080.

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

Можно ли сопоставить определенные шаблоны URL-адресов с разными поведением? Я хочу получить сервер типа REST, т.е. Вызов localhost: 8080/person/1 запустит getPersonHandler (int)

[Mapping("*:8080/person/$id")]
public void getPersonHandler(int id){...}

Синтаксис Mapping - это только моя собственная желаемая аналогия с библиотеками JAX-RS, которые я знаю. Я хотел бы сделать то же самое на С# (рабочий стол С#, а не asp)

4b9b3361

Ответ 1

Вы можете получить аналогичный эффект без атрибутов

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

Использование:

http://localhost:8080/getPersonHandler/333

если вы действительно хотите использовать атрибуты, тогда

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

Затем вы можете использовать как http://localhost:8080/Person/333, и ваши определения будут

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

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

Ответ 2

Если вы работаете в .NET 4.0 или выше и ищете уже существующее REST-серверное решение, которое вы можете подключить (что похоже на вас), вы можете проверить Grapevine. Вы можете получить его с помощью NuGet, а вики проекта содержит множество примеров кода. Кроме того, это открытый исходный код, поэтому, если вы просто хотите посмотреть, как это можно сделать, вы можете увидеть там весь исходный код.

Вы можете фильтровать запросы по информации о пути (с использованием регулярных выражений) и методам запроса (GET, POST и т.д.).

Я автор проекта, и у меня была такая же потребность, как и описанная вами. Используя ресурсы, которые я нашел здесь и где-то еще, я построил Grapevine так, чтобы у меня было решение в моем заднем кармане, когда мне это было нужно снова (DRY).