【问题标题】:URL mapping with C# HttpListener使用 C# HttpListener 进行 URL 映射
【发布时间】:2012-04-04 18:59:42
【问题描述】:

在下面的代码中,我正在等待对 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 将启动 getPersonH​​andler(int)

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

Mapping 语法只是我自己对我所知道的 JAX-RS 库的一厢情愿的类比。我想在 C# 中做同样的事情(桌面 C#,而不是 asp)

【问题讨论】:

  • 你真的需要重新发明轮子吗? ASP.NET MVC 4 中的 Web API 可以做到这一点。
  • 我需要一个独立的应用程序。
  • 仅供参考 ASP.NET Web API 可以自托管(无 IIS)
  • 如果 ASP.NET Web API 可以在可执行文件中自托管,我想看看! (不是说不能,只是不知道可以)。

标签: c# http url-mapping


【解决方案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);
}

【讨论】:

  • 这个@param 变量名是什么?
  • 因为params是c#中的保留字,所以我使用@params
  • 我不知道你可以在变量名中使用@,谢谢。我喜欢这个答案。非常感谢。
  • 最后一件事:你也可以按请求类型(GET,PUT ...)过滤吗?
  • @elmes ctx.Request.HttpMethod
【解决方案2】:

如果您正在使用 .NET 4.0 或更高版本并正在寻找可以插入的预先存在的 REST 服务器解决方案(听起来像您),您可能需要查看 Grapevine。您可以使用 NuGet 获取它,project wiki 有很多示例代码。另外,它是开源的,所以如果你只是想看看它是如何实现的,你可以在那里看到所有的源代码。

您可以通过路径信息(使用正则表达式)和请求方法(GET、POST 等)过滤请求。

我是项目作者,我和你描述的有类似的需求。使用我在这里和其他地方找到的资源,我构建了 Grapevine,这样当我再次需要它时,我就可以在我的后兜里找到一个解决方案 (DRY)。

【讨论】:

  • 不错的项目 - 我需要将 REST API 放入命令行应用程序,Grapevine 比 C# 中的库存 HttpListener 类干净得多。
猜你喜欢
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多