如何通过编程实现WCF的web.config重写规则替代方案
用C#代码实现WCF的URL重写逻辑替代web.config规则
我原本在web.config中配置了两条URL重写规则来处理WCF服务的请求:
<system.webServer> <rewrite> <rules> <!-- Rewrite requests to /MyService to /MyService.svc --> <rule name="MyService" stopProcessing="false"> <match url="MyService/(.*)" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" /> </conditions> <action type="Rewrite" url="/MyService.svc/{R:1}" /> </rule> <!-- Remove path /MoreServices from url --> <rule name="example" stopProcessing="true"> <match url=".*(MoreServices)(.+)" ignoreCase="true" /> <action type="Rewrite" url="{R:2}" /> </rule> </rules> </rewrite> </system.webServer>
规则说明
- 第一条规则:将不带
.svc后缀的请求(如/MyService/xxx)重写为对应的服务地址/MyService.svc/xxx,同时排除实际存在的文件或目录请求。 - 第二条规则:移除URL中的
/MoreServices路径段(如/abc/MoreServices/def会被重写为/def),且匹配后停止后续规则处理。
现在部署的站点不允许在web.config中使用<rule>标签,以下是两种C#代码实现方案:
方案一:自定义HttpModule(IIS托管场景)
通过ASP.NET管道拦截请求,手动处理URL重写,适配IIS托管的WCF服务:
- 创建自定义HttpModule类:
using System; using System.IO; using System.Text.RegularExpressions; using System.Web; namespace YourNamespace { public class WcfUrlRewriteModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += OnBeginRequest; } private void OnBeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; var request = app.Context.Request; var originalUrl = request.Url.AbsolutePath; // 优先处理第二条规则:移除/MoreServices路径,匹配后停止后续处理 var moreServicesRegex = new Regex(@".*(MoreServices)(.+)", RegexOptions.IgnoreCase); var moreServicesMatch = moreServicesRegex.Match(originalUrl); if (moreServicesMatch.Success) { var newUrl = moreServicesMatch.Groups[2].Value; app.Context.RewritePath(newUrl); return; } // 处理第一条规则:重写MyService请求到MyService.svc var myServiceRegex = new Regex(@"^/MyService/(.*)$", RegexOptions.IgnoreCase); var myServiceMatch = myServiceRegex.Match(originalUrl); if (myServiceMatch.Success) { var filePath = app.Context.Server.MapPath(originalUrl); // 排除实际存在的文件或目录 if (!File.Exists(filePath) && !Directory.Exists(filePath)) { var newUrl = $"/MyService.svc/{myServiceMatch.Groups[1].Value}"; app.Context.RewritePath(newUrl); } } } public void Dispose() { // 按需清理资源 } } }
- 在web.config中注册模块(此配置不涉及
<rule>标签,多数托管环境允许):
<system.webServer> <modules> <add name="WcfUrlRewriteModule" type="YourNamespace.WcfUrlRewriteModule" /> </modules> </system.webServer>
方案二:自定义ServiceHostFactory(自托管/深度WCF扩展场景)
通过扩展WCF的ServiceHostFactory实现地址映射,适用于自托管或需要贴近WCF运行时的场景:
- 创建自定义Factory及相关扩展类:
using System; using System.IO; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.ServiceModel.Web; using System.Text.RegularExpressions; namespace YourNamespace { public class CustomServiceHostFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { var host = base.CreateServiceHost(serviceType, baseAddresses); host.Description.Behaviors.Add(new UrlRewriteBehavior()); return host; } } public class UrlRewriteBehavior : IServiceBehavior { public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (var channelDispatcher in serviceHostBase.ChannelDispatchers) { if (channelDispatcher is WebHttpDispatcher webDispatcher) { webDispatcher.DispatchRuntime.MessageInspectors.Add(new UrlRewriteInspector()); } } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } } public class UrlRewriteInspector : IDispatchMessageInspector { public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { var httpProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; var originalUrl = httpProp.RequestUri.AbsolutePath; // 处理第二条规则 var moreServicesRegex = new Regex(@".*(MoreServices)(.+)", RegexOptions.IgnoreCase); var moreServicesMatch = moreServicesRegex.Match(originalUrl); if (moreServicesMatch.Success) { var newUri = new Uri(httpProp.RequestUri.AbsoluteUri.Replace(moreServicesMatch.Value, moreServicesMatch.Groups[2].Value)); httpProp.RequestUri = newUri; return null; } // 处理第一条规则 var myServiceRegex = new Regex(@"^/MyService/(.*)$", RegexOptions.IgnoreCase); var myServiceMatch = myServiceRegex.Match(originalUrl); if (myServiceMatch.Success) { var filePath = System.Web.HttpContext.Current?.Server.MapPath(originalUrl); if (filePath != null && !File.Exists(filePath) && !Directory.Exists(filePath)) { var newPath = $"/MyService.svc/{myServiceMatch.Groups[1].Value}"; var newUri = new Uri(httpProp.RequestUri.AbsoluteUri.Replace(originalUrl, newPath)); httpProp.RequestUri = newUri; } } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { } } }
- 在.svc文件中指定自定义Factory:
<%@ ServiceHost Language="C#" Debug="true" Service="YourNamespace.MyService" Factory="YourNamespace.CustomServiceHostFactory" %>
内容的提问来源于stack exchange,提问作者Robert Smith
相关产品推荐
相关产品推荐

