WCF Service unable to call from .NET Core - wcf

I am doing something wrong and I can't figure it out ... I made .NET Framework 4 console application to communicate with SOAP Service, with use of Topshelf I deployed service on a server and with simple URL access to a method or use of Boomerang tool, I can see service is returning value
URL: http://35.231.17.237:8066/ERPCommunicationService/OriginalService/IsServiceHealthy
But now, when I try to access same service, from .NET Core project, I keep getting error:
System.ServiceModel.ProtocolException:
The remote server returned an unexpected response: (405) Method Not Allowed.
at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(
SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(
String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.
<CreateGenericTask>b__0(IAsyncResult asyncResult)
--- End of stack trace from previous location where exception was thrown ---
Code is simple, I successfully used service endpoint to connect it to .NET Core project, where I can see Reference.cs autogenerated file and all methods from service are there ...
Here is service call from client side (.net core):
public async Task<bool> IsServiceHealthy()
{
try
{
string servicesUrl = $"{_iConfiguration["servicesUrl"]}/IsServiceHealthy";
//My binding setup, since ASP.NET Core apps don't use a web.config file
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
binding.MaxReceivedMessageSize = 10485760;
binding.SendTimeout = new TimeSpan(0, 0, 0, 180);
binding.ReceiveTimeout = new TimeSpan(0, 0, 0, 180);
var rsExec = new OriginalService.OriginalServiceClient(binding,
new EndpointAddress(servicesUrl));
var clientFactory = rsExec.ChannelFactory.CreateChannel();
var response = await clientFactory.IsServiceHealthyAsync();
return response;
}
catch (Exception ex)
{
logging.LogError(ex.ToString());
throw ex;
}
}
And code from server side (.NET Framework 4):
Interface:
[OperationContract]
[WebInvoke(Method = "GET",
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/IsServiceHealthy")]
bool IsServiceHealthy();
Implementation:
public bool IsServiceHealthy()
{
bool serviceResult = false;
byte[] test = new byte[200];
var client = new ChannelFactory<BisWebWS.BisWebWSSOAPPortType>("BisWebWSSOAPPort")
.CreateChannel();
BisWebWS.tauthStrct auth = ServisBasic.GetAuth();
try
{
var result = client.wsTest(new BisWebWS.wsTestRequest(test));
serviceResult = result.wsTestResult;
}
catch (Exception ex)
{
logger.LogError(ex.InnerException.ToString());
}
return serviceResult;
}
When ever I google shown error, everywhere it says its server side setup, but I am kinda stuck as I installed everything there is ... I am using MS Windows Server 2012 R2 Datacenter,
Thank you for shared idea how to fix this problem

The way that we call the service by using the proxy class is an Http Post request, while there is a GET decoration on the method. It requires an Http Get request instead of Post request. This might directly cause the issue.
[OperationContract]
[WebInvoke(Method = "GET",
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/IsServiceHealthy")]
bool IsServiceHealthy();
If the server host the service by using Webhttpbinding, we could directly get the result by typing the service address in the browser address bar since the default request is an Http Get request (your link is not available).
This kind of service is called Restful-style service.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/wcf-web-http-programming-model
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
The typical call is to construct an HTTP request with a request body by using HttpClient library.
We could also call the service by using the client proxy class, like what you do. However, we should keep the binding configuration consistent between the server and the client.
WCF: There was no endpoint listening at, that could accept the message
It is too complex to call the service by using client proxy class, it is better to send Http request with HttpClient when we call Restful style service.
Besides, we could also host the service by using BasicHttpBinding, this may simplify the call. There is no need to add webhttpendpoint behavior and no need to add additional [Webget] decorations.
Simply speaking, we should maintain the binding consistent between the server and the client when using client proxy.
Feel free to let me know if the problem still exists.

Related

Service Fabric Asp.net Core Kestrel HttpClient hangs with minimal load

I have a barebone Service Fabric Application hosting a Asp.net Core 1.1 Web API with Azure Application Gateway as reverse proxy on a Virtual Machine scale set of 5 DS3_V2.
The API have 10 HttpClients with different URLs injected via Dependency Injection.
A simple foreach cycle in a method call 10 Httpclients in parallel:
var cts = new CancellationTokenSource();
cts.CancelAfter(600);
//Logic for asyncronously parallel calling the Call method below
public async Task<MyResponse> Call(CancellationTokenSource cts, HttpClient client, string endpoint )
{
var endpoint = "finalpartOfThendpoint";
var jsonRequest = "jsonrequest";
try
{
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
await content.LoadIntoBufferAsync();
if (cts.Token.IsCancellationRequested)
{
return new MyResponse("Token Canceled");
}
var response = await client.PostAsync(endpoint, content, cts.Token);
if (response.IsSuccessStatusCode && ((int)response.StatusCode != 204))
{
//do something with response and return
return MyResponse("Response Ok")
}
return MyResponse("No response")
}
catch (OperationCanceledException e)
{
return new MyResponse("Timeout");
}
}
There is a single CancellationToken for all calls.
After 600ms, the still pending HttpCalls are canceled and a response is sent back anyway.
In local and in production all works perfectly, all endpoints are called and return in time, rarely one is canceled before the timeout.
But when the number of concurrent connections reach 30+, ALL calls timeout no matter what, until I reduce the load.
Does Asp.net Core have a connection limit?
This is how I create the HttpClients in a custom factory for injection in the main Controller:
public static HttpClient CreateClient(string endpoint)
{
var client = new HttpClient
{
BaseAddress = new Uri(endpoint)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
All the Httpclients are reused and static.
The same exact code works perfectly on a Asp.net Web API 2 hosted on OWIN in Service Fabric. The problem is only with Asp.net Core 1.1
I saw online to create a HttpClientHandler, but there is no parameter for concurrent connections.
What can I do to investigate further?
No exception are thrown but the OperationcanceledException and If I remove the CancellationToken the calls are stuck and the CPU goes to 100%, basically 30 connections destroy the power of 5 quad core servers.
This has something to do to the number of calls going out of Kestrel.
UPDATE
I tried with WebListener and the problem is still present, so it's not Kestrel, but Asp.net Core
I figured it out.
Asp.net core still have some HttpClient limits for the connection to the same server like the old Asp.net WebAPI.
It's poor documented but the old ServicepointManager option for maxconnections must now be passed via HttpClientHandler.
I just create HttpClient like this and the problem vanished.
var config = new HttpClientHandler()
{
MaxConnectionsPerServer = int.MaxValue
};
var client = new HttpClient(config)
{
BaseAddress = new Uri('url here')
};
Really if someone of the team is reading, this should be the default.

Configuring GET Request from Nifi

I am trying to access a WCF service from a browser. I am sending a GET request from my browser to a WCF service. For your reference, the detail is as follows of a WCF service is as follows.
The Service Contract definition is as follows:
[ServiceContract]
public interface IBZTsoftsensor_WcfService {
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, UriTemplate = "json/?inputModel={inputModel}")]
string ExecuteModelJson(string inputModel);
}
And the implementation of this interface is as follows:
public string ExecuteModelJson(string inputModel){
try
{
BZTsoftsensor_ModelInput input = JsonConvert.DeserializeObject<BZTsoftsensor_ModelInput>(inputModel);
var results = this.ExecuteModel(input);
return JsonConvert.SerializeObject(results);
}
catch (Exception ex)
{
return ex.Message;
}
}
When I am accessing this WCF Service from browser with the URL
http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/json/?inputModel={"Pyro":"30.0","O2":"20.0"}
My WCF service is responsing successfully.
However, Using the above URL, when I am configuring GeTHTTP Nifi processor, the processor is erroring illegal characters in GET request URL.
Could you please advise me - what changes I have to made in GET URL , while using GetHTTP processor?
You may need to encode your inputModel parameter, you can use the urlEncode method of NiFi Expression Language:
https://nifi.apache.org/docs/nifi-docs/html/expression-language-guide.html#urlencode
Try this as the URL property:
http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/json/?inputModel=${literal("{\"Pyro\":\"30.0\",\"O2\":\"20.0\"}"):urlEncode()}
Alternatively since your URL is fixed you can just encode it using an online encoding tool, which gives something like this:
http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/json/?inputModel=%7B%22Pyro%22%3A%2230.0%22%2C%22O2%22%3A%2220.0%22%7D%20

Call a WCF Service using just manual code (no config or autogen code)

I am loosely following the method in WCF The Right Way ... The Manual Way to setup my WCF Service.
I have a manually generated proxy class that looks like this:
// Setup a client so we can call our web services.
public class EmployeeClient :IEmployeeService
{
private readonly IEmployeeService EmployeeChannel;
public EmployeeClient(Binding binding, string address)
{
var endpointAddress = new EndpointAddress(address);
EmployeeChannel = new ChannelFactory<IEmployeeService>
(binding, endpointAddress).CreateChannel();
}
public EmployeeResponse SaveOrUpdateEmployee(EmployeeContract employee)
{
return EmployeeChannel.SaveOrUpdateEmployee(employee);
}
}
I then want to call some of these services. But I don't want to use any config files (I am setting up some integration tests and I don't want more dependencies than needed.)
I am currently trying to call them like this:
serviceHost = SelfServiceHost.StartupService();
employeeClient = new EmployeeClient(new BasicHttpBinding(),
SelfServiceHost.StartUpUrl);
EmployeeResponse employeeResponse = employeeClient.SaveOrUpdateEmployee(emp);
When I do that I am getting this exception:
System.ServiceModel.ProtocolException: Content Type text/xml; charset=utf-8 was not supported by service http://localhost:8090/EmployeeService. The client and service bindings may be mismatched. ---> System.Net.WebException: The remote server returned an error: (415) Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'..
What do I need to do to get a call to my service working with code only?
From what you dessribe the binding is not configured in a compatible way.
I suspect that the WCF host has wsHttpBinding and your client-side has BasicHttpBinding or similar...
see http://social.msdn.microsoft.com/forums/en-US/wcf/thread/f29cd9c8-3c89-43d2-92ae-d2a270ab86b9/

Trying to follow WCF delegation example on MSDN but keep getting "impersonation level" exception

Near the bottom of this article (MSDN) in a section entitled "The following code example demonstrates how to use delegation." where MSDN shows an example of how to perform delegation. I have tried to take this example and apply it to my code. In my situation, I have a client app (WCFTestClient), a middle service and a back end service. The goal is is to have the client execute a WCF exposed method on the middle service which in turn calls another method on the back end service. I'm trying to get the identity of the execution on both middle service and back end service to be that of the user executing the client:
Client ----> Middle Service ----> Back End Service.
Here is the exception that occurs on the "channel.PreparePolicy" invocation:
Could not load file or assembly 'System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. Either a required impersonation level was not provided, or the provided impersonation level is invalid. (Exception from HRESULT: 0x80070542)
Here is my code, taken most directly from the example. I did add one line that differs from the MSDN example in my attempt to debug channelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
but to no effect.
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void PreparePolicy(string requestGuid, string policyName, ulong version)
{
WindowsIdentity callerWindowsIdentity = ServiceSecurityContext.Current.WindowsIdentity;
if (callerWindowsIdentity == null)
{
throw new InvalidOperationException
("The caller cannot be mapped to a Windows identity.");
}
using (callerWindowsIdentity.Impersonate())
{
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
Uri uri = new Uri(String.Format("net.tcp://{0}:{1}/App", "10.192.12.159", 8080));
EndpointAddress backendServiceAddress = new EndpointAddress(uri);
ChannelFactory<Service> channelFactory = new ChannelFactory<Service>(binding, backendServiceAddress);
channelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
Service channel = channelFactory.CreateChannel();
channel.PreparePolicy("alkdjf", policyName, version);
}
}
I was using the WCFTestClient as my client in this scenario. Turns out its not enabled to allow delegation. I wrote my own client and enabled it for delegation and everything worked fine.

How can I read the custom HTTP status code using WCF REST?

I'm using the ChannelFactory in WCF to call into a REST service and I want to determine whether the server returned HTTP 200 or 201 in response to a PUT call. Currently, the call succeeds, but I can't determine if my object was created or updated. How can I do this?
WCF is designed for all sorts of channels so this is not a high level object
You can access it though with something like this
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
IMyContract proxy = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)proxy)) {
proxy.MyMethod("Some data"));
var responseCode = WebOperationContext.Current.IncomingResponse.StatusCode;
}
((IClientChannel)proxy).Close();
factory.Close();