如何在WCF中为自动生成的代理添加SOAP消息头?
Great question! When working with auto-generated WCF proxies (like FooServiceClient), you have a few solid options to replicate the header injection logic from your custom UserClientBase class. Let's break down the most reliable approaches:
Approach 1: Use an IClientMessageInspector (Best Practice)
This method intercepts every outgoing message from the client and adds your header consistently—perfect for async scenarios and multiple method calls on the same client instance.
First, create a message inspector that adds the username header:
public class UserNameHeaderInspector : IClientMessageInspector { // Runs before every request is sent public object BeforeSendRequest(ref Message request, IClientChannel channel) { string userName = Thread.CurrentPrincipal.Identity.Name; var header = new MessageHeader<string>(userName); request.Headers.Add(header.GetUntypedHeader("String", "System")); return null; } // No action needed after receiving a reply public void AfterReceiveReply(ref Message reply, object correlationState) {} }
Next, create an endpoint behavior to attach the inspector:
public class UserNameHeaderBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) {} public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new UserNameHeaderInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {} public void Validate(ServiceEndpoint endpoint) {} }
Now, attach the behavior to your client before making calls:
using (var client = new FooServiceClient()) { client.Endpoint.Behaviors.Add(new UserNameHeaderBehavior()); return await client.Test(); }
Caveat: If you're using async/await in an environment like ASP.NET Core, Thread.CurrentPrincipal might not reliably reflect the current user. In that case, use HttpContext.User.Identity.Name instead (ensure you have access to the HttpContext).
Approach 2: Extend the Auto-Generated Client via Partial Class
Since auto-generated WCF proxies are partial classes, you can add custom constructor logic without modifying the generated code. This mirrors your original base class approach:
public partial class FooServiceClient { // Match the constructors from the auto-generated code public FooServiceClient() : base() { SetupUserNameHeader(); } public FooServiceClient(string endpointConfigurationName) : base(endpointConfigurationName) { SetupUserNameHeader(); } // Add other constructor overloads as needed public FooServiceClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { SetupUserNameHeader(); } private void SetupUserNameHeader() { string userName = Thread.CurrentPrincipal.Identity.Name; var header = new MessageHeader<string>(userName); // Wrap the scope in a using to clean up properly (fixes a potential issue in your original code) using (new OperationContextScope(this.InnerChannel)) { OperationContext.Current.OutgoingMessageHeaders.Add( header.GetUntypedHeader("String", "System")); } } }
Now you can use the client exactly as you intended, with headers added automatically:
using (var client = new FooServiceClient()) { return await client.Test(); }
Note: I added a using statement around the OperationContextScope to ensure it's disposed properly—this prevents unintended side effects with the thread's operation context.
Approach 3: Wrap Client Calls in a Helper Method
If you prefer not to modify the client class, create a reusable helper that sets up the header for each call:
public static async Task<T> CallServiceWithUserNameHeader<T>(Func<FooServiceClient, Task<T>> serviceCall) { using (var client = new FooServiceClient()) { string userName = Thread.CurrentPrincipal.Identity.Name; var header = new MessageHeader<string>(userName); using (new OperationContextScope(client.InnerChannel)) { OperationContext.Current.OutgoingMessageHeaders.Add( header.GetUntypedHeader("String", "System")); return await serviceCall(client); } } }
Use it like this:
return await CallServiceWithUserNameHeader(client => client.Test());
This keeps your client usage clean but requires you to use the helper for every service call.
内容的提问来源于stack exchange,提问作者Ish Thomas




