65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 2.0 (C#) 中的 REST Web 服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (12投票s)

2008年9月26日

CPOL

7分钟阅读

viewsIcon

211242

downloadIcon

14

本文介绍了如何在 ASP.NET 2.0 (C#) 中“作弊”实现优雅 URL 的 REST Web 服务。

注释

  1. 我已经被告知,在 .NET 3.5 中有更流畅的方法来实现此功能。我在文章后面也提到了,但请注意,这只是一个仅适用于 2.0 的变通方法。
  2. 我收到警告说 IIS6.0 不喜欢这种变通方法。我只在 IIS 5.1 上尝试过,看起来没问题。我会在可能的时候进行更多测试,并将结果告知大家。结果显示 IIS 6.0 和 5.1 都需要一些调整才能读取没有文件扩展名的 URL。6.0 的处理过程可以在本文的评论中找到。5.1 版本需要以下步骤:
    • 在 IIS 中打开网站属性
    • 选择“主目录”选项卡
    • 点击“配置”按钮
    • 在新窗口中选择“映射”选项卡
    • 点击“添加”按钮
    • 在新窗口的“可执行文件”字段中,浏览到 ASP.NET ISAPI DLL(通常是 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll 之类的路径)
    • 在“扩展名”字段中,输入 .*
    • 取消勾选“检查文件是否存在”框,然后点击“确定”

注意:有一个 bug,如果您的“确定”按钮未激活,您需要点击“可执行文件”字段文本框,您应该会看到文本框中间部分的文本从 /.../ 变为完整文件路径。这应该会使“确定”按钮变为激活状态。

感谢 Neil Kilbride 的分享。

引言

像许多人一样,我最初使用 SOAP Web 请求,它们确实能很好地集成到 IDE 中,但对于互联网一代来说并不直观。很多人,例如雅虎,希望他们的服务能够以任何傻瓜都能理解或预测其可能行为的方式工作。毕竟,如果你知道互联网上肯定会找到什么,那就是各种傻瓜。

所以,我们不再向服务器抛出一个巨大的 SOAP 球,然后服务器又向你吐回它自己的 SOAP 球,而是来看看 REST。

如果你真的想知道 REST 代表什么:Representational State Transfer(表述性状态转移)。

我敢打赌你现在感觉好多了。

不。也许并没有。

我经常发现互联网教程中缺乏实际操作的答案,所以如果你已经读到这里,我猜你已经熟悉或乐意接受格式化的 Web 请求……

www.someserviceprovider.com/myrestservice/3

……并返回一些方便的 XML 或类似的数据块。

是的。归根结底,REST 请求是一种服务,它用简单、常见的 URL 替换了你的 SOAP 泡沫。

它也只是返回一些 XML 或格式化文本或其他内容,而不是匹配的 SOAP 气泡。还有什么能比这更简单呢?在地址栏中输入 URL,就能挖掘到一些有用的内容。

好吧,虽然从用户的角度来看它 *很* 简单,但学习如何实际操作又是另一个问题了。

您要做的是实现一个名为 IHttpHandler 的接口,而获取一个可用的存根最简单的方法是转到一个 Web 项目并添加一个类型为通用处理程序(*.ashx 文件扩展名)的新项。当您这样做时,它将返回一个空白服务,看起来像这样

<%@ WebHandler Language="C#" Class="example" %>

using System;
using System.Web;

public class exampleRestService : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

基本上,您将代码添加到“ProcessRequest”方法中,并且它可以使用其 HttpContext 对象向屏幕回写一些内容,可以是(如本例所示)一些文本,或者一个 XML 文档或其他任何内容。与任何普通 Web 请求一样,您在 URL 中提交的内容是一个 GET 请求,这与访问网站的唯一区别是您不是请求一个文件,而是请求一个对象的当前状态(即服务)。

然而,当您使用通用处理程序时,访问它的 URL 将类似于

www.someserviceprovider.com/exampleRestService.ashx

它将完成其余的工作。

所以这显然是指向一个文件,这不像我们想要的通过在 URL 结构中暗示没有实际文件来引用那样优雅。

然而,既然我们面前有这个可接受但笨重的家伙,让我们让它在您戳它时做一些事情,因为在这个阶段更容易理解。

<%@ WebHandler Language="C#" Class="example" %>

using System;
using System.Web;

public class exampleRestService : IHttpHandler {
    
    public void ProcessRequest (HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string output = "";

        if (context.Request["input"] != null)
        {
                output = context.Request.QueryString["input"].ToString();
        	context.Response.Write("Your Input Was: " + output);
        }
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

这基本上意味着当有人访问

www.someserviceprovider.com/exampleRestService.ashx?input=hello%20world

那么他们将得到的响应是

Your Input Was: hello world

所以效果都很好,但执行方面还有些不尽如人意。我们如何获得最初想要的那种更令人愉悦的 URL 格式呢?

首先,您需要修改 web.config 文件。您需要的段落取决于您的 IIS 是 6.0 或更早版本,还是 7.0。如果是后者,您需要在 < system.web > 段落中寻找一个名为 < handlers > 的段落。如果是前者,该段落位于相同的父级位置,但现在名为 < httpHandlers >

您需要放入该部分的条目看起来像这样

IIS 6.0 and previous: <add type="MyWebServices.exampleRestService, 
	MyWebServices" path="exampleRestService/*" verb="*">

IIS 7.0: <add type="MyWebServices.exampleRestService, 
	MyWebServices" path="exampleRestService/*" verb="*" 
	name="exampleRestService">

对我们而言,此标签中最重要的属性是标记为“type”的属性。我将简要介绍其余部分:verb= 这个处理程序是只针对 GET 吗?只针对 POST 吗?还是两者都(*)?所以这个是两者都;path= 域在重新路由到此服务时寻找什么?在本例中,路径是“exampleRestService/”,然后可以是空字符串,也可以是单个字符,或者是您通过电子邮件收到的最新搞笑互联网备忘录的文本(因为即使 IIS 也偶尔需要大笑一下)。

但是接下来我们到了 type 属性(哦,IIS 7 的用户们,我没有运行 IIS 7,所以 name 属性的作用我不太清楚……命名它?我想,对于某些人来说,这无疑是一个很棒的好处。)它有什么作用?它能施展什么魔法?哦,逗号前面的第一部分告诉它要查找哪个命名空间以及在该命名空间中它试图找到哪个类。逗号后面的部分指的是它将要查找的程序集。

我能看到你们都让我在这里倒退几步。

哪个程序集?我听到你问。我回答说,就是你即将创建的那个。

你看,要进行 URL 处理,你不能使用 *.ashx 文件,它必须消失。抱歉。相反,你将要做的是启动一个新的 C# 类库项目。为了与本文保持一致,你可以将其命名为 MyWebServices

然后,在你的类库中,你可能想把你的类命名为 exampleRestService.cs

它会看起来像这样

using System;
using System.Web;

namespace MyWebServices
{
 public class exampleRestService : IHttpHandler {
    
    public void ProcessRequest (HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string output = "";

        if (context.Request["input"] != null)
        {
                output = context.Request.QueryString["input"].ToString();
        	context.Response.Write("Your Input Was: " + output);
        }
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
 }
}

是的,基本上就是没有 ASP.NET 头部的 ASHX。没错。
将此编译到一个方便的位置,然后回到您的 Web 项目中,创建一个对您刚刚编译的 DLL 的引用。

现在一切都清楚了,对吗?

嗯,不。

因为你仍然会注意到,为了得到正确的输出,URL 必须是

www.someserviceprovider.com/exampleRestService/?input=hello%20world

如此接近……却又不够接近。

我们距离目标只差一个“?input=”。这个问题似乎难以解决。显然,在 .NET 3.5 或那些时尚人士现在称之为的版本中,有一种方法可以自动处理这个问题。我不知道,我还在用 2.0。

我所知道的是,解决这个问题的最好方法可能是作弊。

是的,你听到了。作弊。

我们都知道,如果你有一个动态网页的输入,它应该以 GETPOST 变量的形式出现。这很好,但 GET 变量总是跟在那个恼人的“?”后面,然后你需要用一个名字(比如 input)来标记每个变量,然后用“=”连接你要发送的内容。

如果你只有一个输入变量,有一种方法可以绕过这个问题。

就是这样

string output = "";
int subPos = 0;

output = context.Request.RawUrl.ToString();
if(output.IndexOf("/exampleRestService/") > -1)
{
        subPos = output.IndexOf("/exampleRestService/");
        subPos = subPos + 20;
	output = output.Substring(subPos);
}

context.Response.Write("Your Input Was: " + output);

这并不是最健壮的解决方案,但是,相信我,它有效。也许在我告诉你开始创建类库之类的东西之后你就没再听我说了,但也许你想再多努力一点。如果是这样,这就是一种方法。我很想听听更好的主意,因为我不确定这个方案长期来看会有多健壮。

不过就目前而言……

www.someserviceprovider.com/exampleRestService/the%20end

……会告诉你

Your Input Was: the end 
© . All rights reserved.