使用 ASP.NET Web 服务 (ASMX)

下载示例 下载示例

ASMX 提供了使用简单对象访问协议 (SOAP) 生成发送消息的 Web 服务的功能。 SOAP 是一种独立于平台且与语言无关的协议,用于生成和访问 Web 服务。 ASMX 服务的使用者无需了解用于实现该服务的平台、对象模型或编程语言。 他们只需要了解如何发送和接收 SOAP 消息。 本文演示如何从 Xamarin.Forms 应用程序使用 ASMX SOAP 服务。

SOAP 消息是包含以下元素的 XML 文档:

  • 一个名为 Envelope 的根元素,用于将 XML 文档标识为 SOAP 消息。
  • 一个可选的 Header 元素,其中包含特定于应用程序的信息,例如身份验证数据。 如果 Header 元素存在,它必须是 Envelope 元素的第一个子元素。
  • 一个必需的 Body 元素,其中包含适用于收件人的 SOAP 消息。
  • 用于指示错误消息的可选 Fault 元素。 如果 Fault 元素存在,则它必须是 Body 元素的子元素。

SOAP 可以通过许多传输协议(包括 HTTP、SMTP、TCP 和 UDP)运行。 但是,ASMX 服务只能通过 HTTP 运行。 Xamarin 平台支持通过 HTTP 实现标准 SOAP 1.1,这包括对许多标准 ASMX 服务配置的支持。

此示例包括在物理或模拟设备上运行的移动应用程序,以及提供获取、添加、编辑和删除数据的方法的 ASMX 服务。 运行移动应用程序时,它们会连接到本地托管的 ASMX 服务,如以下屏幕截图所示:

示例应用程序

注意

在 iOS 9 及更高版本中,应用传输安全 (ATS) 强制 Internet 资源 ((例如应用的后端服务器) 和应用)之间的安全连接,从而防止意外泄露敏感信息。 由于 ATS 默认在为 iOS 9 生成的应用中启用,因此所有连接都将受 ATS 安全要求的约束。 如果连接不符合这些要求,则会失败并出现异常。 如果无法对 Internet 资源使用协议和安全通信, HTTPS 则可以选择退出 ATS。 这可以通过更新应用的 Info.plist 文件来实现。 有关详细信息,请参阅 应用传输安全性

使用 Web 服务

ASMX 服务提供以下操作:

操作 说明 parameters
GetTodoItems 获取待办事项的列表
CreateTodoItem 创建新的任务项 XML 序列化的 TodoItem
EditTodoItem 更新待办事项 XML 序列化的 TodoItem
DeleteTodoItem 删除待办事项 XML 序列化的 TodoItem

有关应用程序中使用的数据模型的详细信息,请参阅 对数据建模

创建 TodoService 代理

名为 的 TodoService代理类扩展 SoapHttpClientProtocol 并提供通过 HTTP 与 ASMX 服务通信的方法。 代理是通过向 Visual Studio 2019 或 Visual Studio 2017 中每个特定于平台的项目添加 Web 引用来生成的。 Web 参考为服务的 Web 服务描述语言 (WSDL) 文档中定义的每个操作生成方法和事件。

例如, GetTodoItems 服务操作在代理中 GetTodoItemsAsync 生成方法和 GetTodoItemsCompleted 事件。 生成的方法具有 void 返回类型,并在父SoapHttpClientProtocol类上调用 GetTodoItems 操作。 当调用的方法收到来自服务的响应时,它会触发 GetTodoItemsCompleted 事件,并在事件的 Result 属性中提供响应数据。

创建 ISoapService 实现

为了使共享的跨平台项目能够使用服务,此示例定义了 ISoapService 接口,该接口遵循 C# 中的 Task 异步编程模型。 每个平台实现 以 ISoapService 公开特定于平台的代理。 此示例使用 TaskCompletionSource 对象将代理公开为任务异步接口。 有关使用 TaskCompletionSource 的详细信息,请参阅以下部分中每个操作类型的实现。

示例 SoapService

  1. 将 实例化 TodoService 为类级实例
  2. 创建名为 的 Items 集合以存储 TodoItem 对象
  3. 为 上的可选 Url 属性指定自定义终结点 TodoService
public class SoapService : ISoapService
{
    ASMXService.TodoService todoService;
    public List<TodoItem> Items { get; private set; } = new List<TodoItem>();

    public SoapService ()
    {
        todoService = new ASMXService.TodoService ();
        todoService.Url = Constants.SoapUrl;
        ...
    }
}

创建数据传输对象

示例应用程序使用 TodoItem 类为数据建模。 若要将项存储在 TodoItem Web 服务中,必须先将其转换为代理生成的 TodoItem 类型。 这是由 ToASMXServiceTodoItem 方法完成的,如以下代码示例所示:

ASMXService.TodoItem ToASMXServiceTodoItem (TodoItem item)
{
    return new ASMXService.TodoItem {
        ID = item.ID,
        Name = item.Name,
        Notes = item.Notes,
        Done = item.Done
    };
}

此方法创建一个新 ASMService.TodoItem 实例,并将每个属性设置为实例中的 TodoItem 相同属性。

同样,从 Web 服务检索数据时,必须将其从代理生成的 TodoItem 类型转换为 TodoItem 实例。 这是使用 FromASMXServiceTodoItem 方法完成的,如以下代码示例所示:

static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)
{
    return new TodoItem {
        ID = item.ID,
        Name = item.Name,
        Notes = item.Notes,
        Done = item.Done
    };
}

此方法从代理生成的 TodoItem 类型检索数据,并将其设置在新创建的 TodoItem 实例中。

检索数据

接口 ISoapService 要求 RefreshDataAsync 方法返回 Task 包含项集合的 。 但是, TodoService.GetTodoItemsAsync 方法返回 void。 若要满足接口模式,必须调用 GetTodoItemsAsync,等待 GetTodoItemsCompleted 事件触发,然后填充集合。 这使你可以将有效的集合返回到 UI。

下面的示例创建一个新的 TaskCompletionSource,开始 方法中的 RefreshDataAsync 异步调用,并等待 Task 提供的 TaskCompletionSource。 调用事件处理程序时, TodoService_GetTodoItemsCompleted 它会填充 Items 集合并更新 TaskCompletionSource

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> getRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.GetTodoItemsCompleted += TodoService_GetTodoItemsCompleted;
    }

    public async Task<List<TodoItem>> RefreshDataAsync()
    {
        getRequestComplete = new TaskCompletionSource<bool>();
        todoService.GetTodoItemsAsync();
        await getRequestComplete.Task;
        return Items;
    }

    private void TodoService_GetTodoItemsCompleted(object sender, ASMXService.GetTodoItemsCompletedEventArgs e)
    {
        try
        {
            getRequestComplete = getRequestComplete ?? new TaskCompletionSource<bool>();

            Items = new List<TodoItem>();
            foreach (var item in e.Result)
            {
                Items.Add(FromASMXServiceTodoItem(item));
            }
            getRequestComplete?.TrySetResult(true);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"\t\tERROR {0}", ex.Message);
        }
    }

    ...
}

有关详细信息,请参阅异步编程模型TPL 和传统.NET Framework异步编程

创建或编辑数据

创建或编辑数据时,必须实现 ISoapService.SaveTodoItemAsync 方法。 此方法检测 是新项还是 TodoItem 更新项,并在 对象上 todoService 调用相应的方法。 CreateTodoItemCompleted还应实现 和 EditTodoItemCompleted 事件处理程序,以便了解 何时todoService收到来自 ASMX 服务的响应 (这些处理程序可以合并为单个处理程序,因为它们) 执行相同的操作。 以下示例演示接口和事件处理程序实现,以及 TaskCompletionSource 用于异步操作的对象:

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> saveRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.CreateTodoItemCompleted += TodoService_SaveTodoItemCompleted;
        todoService.EditTodoItemCompleted += TodoService_SaveTodoItemCompleted;
    }

    public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
    {
        try
        {
            var todoItem = ToASMXServiceTodoItem(item);
            saveRequestComplete = new TaskCompletionSource<bool>();
            if (isNewItem)
            {
                todoService.CreateTodoItemAsync(todoItem);
            }
            else
            {
                todoService.EditTodoItemAsync(todoItem);
            }
            await saveRequestComplete.Task;
        }
        catch (SoapException se)
        {
            Debug.WriteLine("\t\t{0}", se.Message);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\t\tERROR {0}", ex.Message);
        }
    }

    private void TodoService_SaveTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        saveRequestComplete?.TrySetResult(true);
    }

    ...
}

删除数据

删除数据需要类似的实现。 定义 、 TaskCompletionSource实现事件处理程序和 ISoapService.DeleteTodoItemAsync 方法:

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> deleteRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.DeleteTodoItemCompleted += TodoService_DeleteTodoItemCompleted;
    }

    public async Task DeleteTodoItemAsync (string id)
    {
        try
        {
            deleteRequestComplete = new TaskCompletionSource<bool>();
            todoService.DeleteTodoItemAsync(id);
            await deleteRequestComplete.Task;
        }
        catch (SoapException se)
        {
            Debug.WriteLine("\t\t{0}", se.Message);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\t\tERROR {0}", ex.Message);
        }
    }

    private void TodoService_DeleteTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        deleteRequestComplete?.TrySetResult(true);
    }

    ...
}

测试 Web 服务

使用本地托管服务测试物理或模拟设备需要自定义 IIS 配置、终结点地址和防火墙规则。 有关如何设置测试环境的详细信息,请参阅配置远程访问IIS Express。 测试 WCF 和 ASMX 之间的唯一区别是 TodoService 的端口号。