WCF rest the URITemplate added automatically - wcf-rest

I have a WCF REST Service with teh following URL:
http:/localhost/GetAllOrders
but the factorychannel always genereates the Uri to http:/localhost/Service.svc/GetAllOrders/GetAllOrders
Here is my client factory channel:
Uri address = new Uri("http:/localhost/Service.svc/GetAllOrders");
var factory = new System.ServiceModel.Web.WebChannelFactory<App.WebService.IOrderSvc>(address);
var webHttpBinding = factory.Endpoint.Binding as System.ServiceModel.WebHttpBinding;
App.WebService.IOrderSvc service = factory.CreateChannel();
var orders = service.GetAll(); // this one throwing an error since the URI has become http:/localhost/Service.svc/GetAllOrders/GetAllOrders
and here is the service contract:
[WebGet(UriTemplate = "GetAllOrders")]
[OperationContract]
List<Order> GetAll();
any idea why the Uritemplate being added twice?

The Uri is wrong - it should be service's Uri. You should write:
Uri address = new Uri("http:/localhost/Service.svc/");

Related

WCF Service unable to call from .NET Core

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.

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

Specifying Castle WCF Integration Facility Endpoint Behavior per Endpoint

I'm using Castle WCF Integration Facility and I have everything working properly for my first webHttp endpoint. For this endpoint to work, it requires that the endpoint have the WebHttpBehavior enabled. I was able to achieve this using:
container.Register(Component.For<IEndpointBehavior>()
.ImplementedBy<WebHttpBehavior>());
This becomes a problem when I try to enable a second endpoint using BasicHttpBinding which is not compatible with the WebHttpBehavior.
Is there someway to specify that the IEndPointBehavior registration above is only applicable to a certain endpoint?
This is my full installer for the service:
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(Component.For<IDiagnosticService>()
.ImplementedBy<DiagnosticService>()
.Named("DiagnosticService")
.LifestyleTransient()
.AsWcfService(new DefaultServiceModel()
.Hosted()
.AddEndpoints(WcfEndpoint.BoundTo(new WebHttpBinding()).At("json"))
.AddEndpoints(WcfEndpoint.BoundTo(new BasicHttpBinding()).At("soap"))
.PublishMetadata(o => o.EnableHttpGet())));
container.Register(Component.For<IEndpointBehavior>()
.ImplementedBy<WebHttpBehavior>());
Ok. I finally figured this out. Turns out that the majority of my problem had to do with the Azure emulation environment rather than Castle WCF Integration. The answer is pretty straight forward -- just setup the ServiceEndpoint instances and use the WcfEndpoint.FromEndpoint() method.
Here is my working installer:
String internalEndpointAddress = string.Format("http://{0}/DiagnosticService.svc",
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Endpoint1"].IPEndpoint);
// This ContractDescription instance must be used for both endpoints in this case
ContractDescription description = ContractDescription.GetContract(typeof(IDiagnosticService));
// Create JSON webHTTP Binding
WebHttpBinding webhttpbinding = new WebHttpBinding();
string jsonURI = internalEndpointAddress + "/json";
EndpointAddress jsonEndpointAddress = new EndpointAddress(new Uri(jsonURI));
ServiceEndpoint jsonEndpoint = new ServiceEndpoint(description, webhttpbinding, jsonEndpointAddress);
jsonEndpoint.Behaviors.Add(new WebHttpBehavior());
// Create WSHTTP Binding
WSHttpBinding wsHttpBinding = new WSHttpBinding();
string soapURI = internalEndpointAddress + "/soap";
EndpointAddress soapEndpointAddress = new EndpointAddress(new Uri(soapURI));
ServiceEndpoint soapEndpoint = new ServiceEndpoint(description, wsHttpBinding, soapEndpointAddress);
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(Component.For<IDiagnosticService>()
.ImplementedBy<DiagnosticService>()
.Named("DiagnosticService")
.LifestyleTransient()
.AsWcfService(new DefaultServiceModel()
.Hosted()
.AddEndpoints(WcfEndpoint.FromEndpoint(jsonEndpoint))
.AddEndpoints(WcfEndpoint.FromEndpoint(soapEndpoint))
.PublishMetadata(o => o.EnableHttpGet())));

WCF 400 Bad Request

I created a simple function
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json)]
string Start();
Definition,
public String Start()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize("Check");
}
From browser using Javascript/Jquery,
http://localhost/service1.svc tells me I have created a service and all other info.. Looks fine.
I'm trying to call this using
http://localhost/service1.svc/Start
I get a 400 bad request for this call. I hope I'm not doing something totally wrong here. I should be able to access WCF service from browser right?
I tried looking a lot before I thought of posting. But I'm unable to get this basic thing working is frustrating me.
EDIT & UPDATE
Now I'm at this stage. The service page is telling me that the metadata service is disabled and is asking me to insert the following text
<serviceMetadata httpGetEnabled="true" />
I inserted the text - but still it shows the same text!! This is getting too confusing now..
Try to change POST with GET and restart the request
Works for me. I created WCF Rest Service.
I use URL which looks like http://localhost:8080/Service1/Start
Here is the code:
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web.Script.Serialization;
namespace WcfRestService1
{
// Start the service and browse to http://<machine_name>:<port>/Service1/help to view the service's generated help page
// NOTE: By default, a new instance of the service is created for each call; change the InstanceContextMode to Single if you want
// a single instance of the service to process all calls.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If the service is renamed, remember to update the global.asax.cs file
public class Service1
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]
public string Start()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize("Check");
}
}
}

Comsuming REST (JSON) API using WCF

I am writing a .NET app that will talk to JSON-based API to pull/push data. I saw similar question asked before:
Consuming a RESTful JSON API using WCF
but I need little more information on the same subject. Here is JSON that I have to send in request:
{"login":{"password":"PASSWORD","username":"USERNAME"}}
and response JSON will be something like:
{"response":{"status":"OK","token":"o9b0jrng273hn0"}}
Here is what I came up with:
[ServiceContract]
public interface ITestApi
{
[OperationContract]
[WebInvoke( Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/login"
)]
LoginResponse Login( LoginRequest login );
}
where LoginRequest has username and password properties and LoginResponse has token property.
When I call the api, request is successful and I get the response back as expected ( I verified this with Fiddler). But WCF is not able to create LoginResponse object for me. it is always null. I believe I am doing somethign wrong, can someone please point me out what I have to do to get this right?
Is this the right way to create a JSON-based REST service client? I am using RESTful api first time, so I do not have more knowledge about it.
Your LoginResponse class should look like something this:
[DataContract]
public class LoginResponse
{
[DataMember]
public string token { get; set; }
}
It needs to be decorated with the DataContract and DataMember attributes so the serializer (DataContractJsonSerializer in the case of JSON ) knows how to serialize it.
EDIT:
Also, your client should be configured to use webHttpBinding and the endpoint behavior should be configured to use webHttp, as in the following example.
Go get the Microsoft.Http client library from the lib folder in this project or from thw WCF REST Starter Kit. Using this you can do:
var client = new HttpClient();
var content = HttpContent.Create("{'login':{'password':'PASSWORD','username':'USERNAME'}}", "application/json");
var response = client.Post("http://service.com/login",content);
var jsonString = response.Content.ReadAsString();
If you don't want to read the Json as a string and parse using something like Json.Net, and you prefer to use DataContracts, you can do:
var loginResponse = response.Content.ReadAsJsonDataContract<LoginResponse>();
Using WCF Channels on the client to deal with REST services is just going cause you far more pain than you really want. You are much better to just stick with plain HTTP.