如何将.NET Framework的WCF服务迁移为独立ASP.NET Web API 2服务
ASP.NET Web API 2 路由配置、请求构造与WCF迁移指南
一、特性路由配置规则
首先明确Web API 2的核心参数绑定与路由逻辑:
- 简单类型(string、int、bool、DateTime等)默认从URL(路径段、查询字符串)按名称匹配绑定,无需额外标记
- 复杂类型(自定义类对象)默认从请求体绑定,单个接口仅支持1个从请求体读取的参数,多个复杂对象必须封装为统一请求DTO
- 特性路由优先级高于传统约定路由,配置
[Route]特性后,路由匹配与Controller名、Action名无直接关联 - HTTP谓词必须匹配操作语义:无副作用的查询操作使用GET,带复杂参数、有数据变更的操作使用POST,禁止所有接口全标记为HttpGet,否则会遇到URL长度限制、代理缓存篡改、敏感参数泄露等问题
正确的Controller代码示例
首先给Controller添加统一路由前缀减少重复配置,针对包含复杂类型的ServiceCallA调整为POST请求,封装多复杂参数为请求DTO:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; // 统一接口前缀,该控制器下所有路由均以/api/sample开头 [RoutePrefix("api/sample")] public class ClientActivationController : ApiController { // 多复杂参数封装为统一请求DTO,解决单FromBody参数限制 public class ServiceCallARequest { public SampleClass1 Param2 { get; set; } public SampleClass2 Param3 { get; set; } } // 含复杂类型,使用POST,param1从查询字符串绑定,复杂对象从请求体绑定 [HttpPost] [Route("call-a")] public Result1 ServiceCallA(string param1, [FromBody] ServiceCallARequest request) { // 原有业务逻辑 } // 全简单类型查询接口,使用GET,参数自动从查询字符串绑定 [HttpGet] [Route("call-b")] public Result1 ServiceCallB(string param1, string param2, bool param3) { // 原有业务逻辑 } [HttpGet] [Route("call-c")] public Result2 ServiceCallC(string param1, string param2) { // 原有业务逻辑 } // 单参数可直接嵌入路由路径,路径占位符{param1}必须和方法参数名一致 [HttpGet] [Route("call-d/{param1}")] public Result2 ServiceCallD(string param1) { // 原有业务逻辑 } }
必要的全局配置调整
修改默认的WebApiConfig配置,统一返回JSON格式,避免XML格式化器干扰:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // 启用特性路由 config.MapHttpAttributeRoutes(); // 移除默认XML格式化器,所有接口默认返回JSON config.Formatters.Remove(config.Formatters.XmlFormatter); // 配置JSON序列化规则,兼容原有WCF字段格式 var jsonSettings = config.Formatters.JsonFormatter.SerializerSettings; jsonSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; jsonSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 保留传统约定路由(非必须,全用特性路由可删除) config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
二、Winforms客户端请求构造方法
首先注意:HttpClient必须全局静态单例初始化,禁止每次调用新建实例,否则会出现套接字耗尽问题。
// 全局初始化一次即可,ServiceUrl替换为实际服务根地址,如https://your-service.com/ private static readonly HttpClient _client = new HttpClient() { BaseAddress = new Uri(ServiceUrl) };
GET请求调用示例
不要手动拼接查询字符串,使用内置类自动处理参数转义:
// 调用ServiceCallB示例 var queryParams = System.Web.HttpUtility.ParseQueryString(string.Empty); queryParams["param1"] = "test1"; queryParams["param2"] = "test2"; queryParams["param3"] = "true"; // bool值直接传True/False字符串即可 var uriBuilder = new UriBuilder($"{ServiceUrl.TrimEnd('/')}/api/sample/call-b"); uriBuilder.Query = queryParams.ToString(); var response = await _client.GetAsync(uriBuilder.Uri); response.EnsureSuccessStatusCode(); // 非200状态码直接抛出异常,无需手动判断 // 需提前Nuget安装Microsoft.AspNet.WebApi.Client包使用ReadAsAsync方法 var result = await response.Content.ReadAsAsync<Result1>();
路由路径嵌入参数的ServiceCallD调用更简单:
var param1 = "test-param"; var response = await _client.GetAsync($"/api/sample/call-d/{Uri.EscapeDataString(param1)}"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsAsync<Result2>();
POST请求调用示例
复杂类型直接序列化为JSON传入请求体,简单参数放在查询字符串:
var queryParams = System.Web.HttpUtility.ParseQueryString(string.Empty); queryParams["param1"] = "test1"; var uriBuilder = new UriBuilder($"{ServiceUrl.TrimEnd('/')}/api/sample/call-a"); uriBuilder.Query = queryParams.ToString(); var requestBody = new ClientActivationController.ServiceCallARequest { Param2 = new SampleClass1 { /* 赋值 */ }, Param3 = new SampleClass2 { /* 赋值 */ } }; var response = await _client.PostAsJsonAsync(uriBuilder.Uri, requestBody); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsAsync<Result1>();
三、WCF到Web API 2 完整迁移要点
- 契约梳理:先剥离原有WCF服务的业务逻辑、数据模型(Result1、Result2、SampleClass1等),删除WCF专属特性(
[AspNetCompatibilityRequirements]、[OperationContract]、[DataContract]等),字段名如果要兼容老客户端,可使用[JsonProperty]标记序列化名称,避免字段名变化导致客户端解析失败。 - 谓词拆分:原有WCF所有请求均为SOAP POST,迁移时按REST语义拆分:查询类无副作用接口用GET,新增/修改/复杂参数接口用POST,全量更新用PUT,删除用DELETE,不要全量使用GET。
- 上下文替换:原有WCF依赖的
OperationContext、会话状态全部替换为Web API的HttpRequestMessage/HttpResponseMessage,业务逻辑不要强依赖HttpContext.Current,提升代码可测试性。 - 调试配置:无需切换.NET Core即可使用Swagger,直接Nuget安装5.x版本的
Swashbuckle包,安装完成后即可通过/swagger路径访问在线接口文档,直接在线调试接口,无需手动构造请求测试。 - 异常处理:原有WCF的SOAP Fault异常机制替换为Web API的
HttpResponseException或全局异常过滤器,统一返回JSON格式错误信息,客户端统一通过HTTP状态码判断请求结果,不要沿用原有WCF的自定义错误码逻辑。 - 部署清理:独立部署时删除项目中所有svc文件、WCF相关配置节点、WCF专属dll,IIS应用程序池选择与项目匹配的.NET Framework版本,确保ASP.NET路由处理程序正常加载,避免原有WCF的svc处理程序拦截Web API请求。
- 认证迁移:原有WCF使用的Windows认证、Forms认证、Token认证可直接在Web API中复用,Windows认证直接在IIS中开启即可,自定义Token认证可通过消息过滤器实现,无需重写认证逻辑。
内容的提问来源于stack exchange,提问作者JohnB
相关产品推荐
相关产品推荐

