You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, I had a similar issue with forcing specific settings for requests. The article you are reading is from 2007 and is for the .NET framework rather than .NET Core.
If you have a look at my issue #3442 you can see how I alter the requests.
var client = new GeneratedSvcUtilClient();
client.Endpoint.EndpointBehaviors.Add(new ForceCertificateEndpointBehavior());
var channel = client.ChannelFactory.CreateChannel
public class ForceCertificateEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
var x5092 = new X509Certificate2("Settings for SHA1 signed certificate");
bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(x =>
{
x.SslProtocols = SslProtocols.Tls12;
x.ClientCertificates.Add(x5092);
return new CustomMessageHandler(x);
}));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
However, are you sure, that the custom handler are gonna be executed? For me binding parameter scope function are not being executed at all. Can you share the complete example?
public class CustomCorrelationDelegatingHandler : DelegatingHandler
{
public CustomCorrelationDelegatingHandler(HttpClientHandler handler)
{
InnerHandler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("xd", "xd");
return base.SendAsync(request, cancellationToken);
}
}
public class CorrelationEndpointBehavior : IEndpointBehavior
{
public CorrelationEndpointBehavior()
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(x =>
{
return new CustomCorrelationDelegatingHandler(x);
}));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
var client = new CalculatorClient();
client.Endpoint.EndpointBehaviors.Add(new CorrelationEndpointBehavior());
var data = client.GetTestDataAsync().GetAwaiter().GetResult();
The usage might look like something like this, where you use FlowingOperationContextScope instead of OperationContextScope, and call ContinueOnScope for every async call in there:
public static async Task<T> WithPreemptiveBasicAuth<TClientInterface, T>(this ClientBase<TClientInterface> client, Func<Task<T>> taskFactory)
where TClientInterface : class
{
using (var scope = new FlowingOperationContextScope(client.InnerChannel))
{
var username = client.ClientCredentials.UserName.UserName;
var password = client.ClientCredentials.UserName.Password;
var headerValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"));
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = $"Basic {headerValue}";
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
return await taskFactory().ContinueOnScope(scope);
}
}
@Zek99 Solved it - didn't work because of the outdated versions in the project - Visual Studio adds them outdated when generating the client. Works with the last 4.5.3 version of the System.ServiceModel.* packages.
@bdrupieski For now EndpointBehavior works for me and seems easier to setup.
@r-pankevicius, I had a quick look at your project. I have an alternative suggestion for you. You could add the headers via a message inspector so you don't need to create an OperationContext scope. Also looking at your code, I don't think you need to make your Intercept method async if you add the constraint that the WCF contract interface only uses Task based async operations. You will need to do quite a bit more reflection code. At the end of your Intercept method, you need to set invocation.ReturnValue to the correct result. Here's where Task's help you. You don't need to actually return the "real" Task result, you just need to return a Task which completes with the same final result as the intercepted method does. So for example, if the return type from an method is Task, then invocation.ReturnValue only needs to be set to a Task which will complete with the right Foo object. It doesn't need to be the original Task instance.
So what you need to do is create a Task InterceptAsync(IInvocation invocation) method which doesn't set invocation.ReturnValue, but instead returns a Task (which is the base type of Task so you don't need to worry about the concrete result type from the sync Intercept method). InterceptAsync is not and async method, but instantiates a TaskCompletionSource. This is where the extra reflection comes in, you need to use reflection to find out what TResult is, then create an instance of TaskCompletionSource for that type. You then have another method async void InterceptAsyncCore(IInvocation invocation, object tcs). This is async void as you won't be awaiting the call.From InterceptAsync, you call InterceptAsyncCore and then return TaskCompletionSource.Task to the Intercept method. The second parameter is an object as TaskCompletionSource is generic and you don't have the generic type at compile time. In InterceptAsyncCore, you can await your m_TokenService.GetTokenAsync call, then call methodToInvoke.Invoke. You then cast the result of Invoke to Task and await it. You will need a try/catch around that. Once it's completed, you use reflection to get the Result from the Task and call tcs.SetResult. If it threw an exception, you call tcs.SetException.
So what you end up with is the Intercept method calls InterceptAsync which returns a Task object (backed by a TaskCompletionSource) which will complete with the result of the call when that call finishes. This task is set as the result and the original caller now has a Task which completes with the correct final result.
Activity
Zek99 commentedon Mar 18, 2019
Hi, I had a similar issue with forcing specific settings for requests. The article you are reading is from 2007 and is for the .NET framework rather than .NET Core.
If you have a look at my issue #3442 you can see how I alter the requests.
CurlyBraceTT commentedon Mar 19, 2019
@Zek99 Big thanks!
However, are you sure, that the custom handler are gonna be executed? For me binding parameter scope function are not being executed at all. Can you share the complete example?
Here is mine code and full projects (Service & Client)
https://drive.google.com/open?id=1Rt0xz-3ZFZ4WMKVmHadlG9Vi4pT5OdB5
Lxiamail commentedon Mar 20, 2019
@CurlyBraceTT @Zek99 Please check out the sample code in this test
Zek99 commentedon Mar 20, 2019
@CurlyBraceTT your
CustomCorrelationDelegatingHandlershould work. Confirmed using Telerik's FiddlerUsing both header properties.
bdrupieski commentedon Mar 25, 2019
@CurlyBraceTT You need to create a
HttpRequestMessagePropertyand use that, like this:Another potential issue is that
OperationContextScopedoesn't work well inasynccode. See this issue #2512. I've been using the solution from here successfully https://stackoverflow.com/questions/18284998/pattern-for-calling-wcf-service-using-async-await/22753055#22753055.The usage might look like something like this, where you use
FlowingOperationContextScopeinstead ofOperationContextScope, and callContinueOnScopefor every async call in there:CurlyBraceTT commentedon Mar 25, 2019
@Zek99 Solved it - didn't work because of the outdated versions in the project - Visual Studio adds them outdated when generating the client. Works with the last
4.5.3version of theSystem.ServiceModel.*packages.@bdrupieski For now
EndpointBehaviorworks for me and seems easier to setup.r-pankevicius commentedon Mar 29, 2019
Thank you guys, that my findings helped you to resolve the issue. Anyone can help me with "final solution" for WCF+Oauth2 in scope of https://github.com/r-pankevicius/WcfClientWithOMiauAuthentification?
The issue is that SlimServiceClientInterceptor.Intercept method is sync while it must be async.
mconnew commentedon Mar 29, 2019
@r-pankevicius, I had a quick look at your project. I have an alternative suggestion for you. You could add the headers via a message inspector so you don't need to create an OperationContext scope. Also looking at your code, I don't think you need to make your Intercept method async if you add the constraint that the WCF contract interface only uses Task based async operations. You will need to do quite a bit more reflection code. At the end of your Intercept method, you need to set invocation.ReturnValue to the correct result. Here's where Task's help you. You don't need to actually return the "real" Task result, you just need to return a Task which completes with the same final result as the intercepted method does. So for example, if the return type from an method is Task, then invocation.ReturnValue only needs to be set to a Task which will complete with the right Foo object. It doesn't need to be the original Task instance.
So what you need to do is create a
Task InterceptAsync(IInvocation invocation)method which doesn't set invocation.ReturnValue, but instead returns a Task (which is the base type of Task so you don't need to worry about the concrete result type from the sync Intercept method). InterceptAsync is not andasyncmethod, but instantiates a TaskCompletionSource. This is where the extra reflection comes in, you need to use reflection to find out what TResult is, then create an instance of TaskCompletionSource for that type. You then have another methodasync void InterceptAsyncCore(IInvocation invocation, object tcs). This isasync voidas you won't be awaiting the call.From InterceptAsync, you call InterceptAsyncCore and then return TaskCompletionSource.Task to the Intercept method. The second parameter is an object as TaskCompletionSource is generic and you don't have the generic type at compile time. In InterceptAsyncCore, you can await your m_TokenService.GetTokenAsync call, then call methodToInvoke.Invoke. You then cast the result of Invoke to Task and await it. You will need a try/catch around that. Once it's completed, you use reflection to get the Result from the Task and call tcs.SetResult. If it threw an exception, you call tcs.SetException.So what you end up with is the Intercept method calls InterceptAsync which returns a Task object (backed by a TaskCompletionSource) which will complete with the result of the call when that call finishes. This task is set as the result and the original caller now has a Task which completes with the correct final result.
r-pankevicius commentedon Mar 30, 2019
@mconnew Thanks, I found IClientMessageInspector in WCF.
Yes, that threading stuff needs more work.
Thanks a lot for the code review, BTW.