There is a self hosted WCF REST service, need to send an xml post message to it.
Seems like this question was asked and answered several times, but after trying every solution I still didn`t get any success.
Server: interface
[ServiceContract]
public interface ISDMobileService
{
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle=WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat=WebMessageFormat.Xml)]
int ProcessMessage(string inputXml);
}
Server: class
public class Service : ISDMobileService
{
public int ProcessMessage(string inputXml)
{
Console.WriteLine( "ProcessMessage : " + inputXml );
return 0;
}
}
Server: hosting
class Program
{
static void Main(string[] args)
{
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://172.16.3.4:7310"));
WebHttpBinding webbind = new WebHttpBinding(WebHttpSecurityMode.None);
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(ISDMobileService), webbind, "");
ServiceDebugBehavior stp = host.Description.Behaviors.Find<ServiceDebugBehavior>();
stp.HttpsHelpPageEnabled = false;
host.Open();
Console.WriteLine("Service is up and running. Press 'Enter' to quit >>>");
Console.ReadLine();
host.Close();
}
}
Request from fiddler without anything in the "Request Body" works just fine and fires break point inside ProcessMessage method of Service class, any variant of data in "Request Body",
e.g.: test || <inputXml>test</inputXml> || inputXml="test" || <?xml version="1.0" encoding="UTF-8" ?><inputXml>test</inputXml> etc. gives HTTP/1.1 400 Bad Request
Will appreciate any help with this
A few things:
Since you're using WebServiceHost, you don't need to explicitly add the service endpoint (call to host.AddServiceEndpoint(...) in your Main.
The operation takes a string parameter; if you want to send it in XML, you need to wrap the string in the appropriate element. Try this body and it should work:
Body:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">This is a string encoded in XML</string>
You can also send it in different formats, such as JSON. This request should work as well
POST http://.../ProcessMessage
Host: ...
Content-Type: application/json
Content-Length: <the actual length>
"This is a string encoded in JSON"
Related
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.
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
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");
}
}
}
I'm building a WCF router and my client uses Reliable Sessions. In this scenario when the client opens a channel a message is sent (establishing a Reliable Session?). Its contents is as follows:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
<a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
<a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
</s:Header>
<s:Body>
<CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<AcksTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</AcksTo>
<Offer>
<Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
<Endpoint>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</Endpoint>
<IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
</Offer>
</CreateSequence>
</s:Body>
</s:Envelope>
The problem here is that the headers do not contain any information I can use to look up what service to route the message to. In Busatmante's router sample code she gets around this by adding a header to the endpoint:
<client>
<endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
bindingConfiguration="wsHttp"
contract="localhost.IMessageManagerService" >
<headers>
<Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
</headers>
</endpoint>
</client>
When the reliable session is opened the message contains this custom header.
<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
This is great; however, I have a requirement to configure the client programatically. I figured that the ChannelFactory Endpoint would have a Header object to which I could manually add my custom header. Unfortunately it does not. So I did some searching and found some recomendations to extend WCF by implementing an IClientMessageInspector to add my header and adding it as a behavior to my endpoint.
public class ContractNameMessageInspector : IClientMessageInspector {
private const string HEADER_NAME = "ContractName";
private readonly string _ContractName;
public ContractNameMessageInspector(string contractName) {
_ContractName = contractName;
}
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState) { }
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
}
}
else {
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
#endregion
}
So when my client makes a service call the message contains the custom header but the message establishing the Reliable Sessions still does not.
So my question is; how do I add a custom header to the Endpoint programatically in such a way that the reliable session message contains it?
Many Thanks
Figured it out. Although there is no property or method to add a header to an EndpointAddress there is an optional parameter on the constructor.
_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());
_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);
Now when I receive the message establishing the reliable session it actually does contain my custom header. This kinda makes sense as the message inspector most likely only operates on dispatched messages while the message establishing the reliable session is generated by lower level WCF code.
This works for me
public TResult Invoke<TResult>(Func<TClient, TResult> func,MessageHeader header)
{
TClient client = default(TClient);
var sw = new Stopwatch();
try
{
sw.Start();
using (client = _channelFactory.CreateChannel())
{
using (OperationContextScope contextScope = new OperationContextScope(client))
{
OperationContext.Current.OutgoingMessageHeaders.Add(header);
return func.Invoke(client);
}
}
}
finally
{
CloseConnection(client);
Instrument(this.GetType().Name, sw);
}
}
To programmatically add address headers see MSDN's Address Headers where one can programmatically add a header such as:
var cl = new MyWCFClientContext();
var eab = new EndpointAddressBuilder(cl.Endpoint.Address);
eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Key
string.Empty, // Namespace
"JabberwockyClient")); // Header Value
cl.Endpoint.Address = eab.ToEndpointAddress();
Can anyone point me to an example how to post a SOAP Request to a WCF Service and return a SOAP Response? Basically a Travel client sends a SOAP request with search parameters and the WCF Service checks within the database and then sends the appropriate holidays.
I keep getting this error, with the method I have used: "The remote server returned an error: (400) Bad Request"
The error you got is because the server does not understand the HTTP request.
It could be the binding you configured or the service proxy is incorrect at client level.
Or the service you defined expects HTTP GET rather than HTTP POST. Sometimes the add service reference may not generate correct HTTP verb for some [WebGet] attributed operations. You may need to add [WebGet] for the operation at client side manually.
Either have a look at SoapUI, or locate the WcfTestClient buried deep in your Visual Studio folders (C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE).
Both can connect to a WCF service and send/receive SOAP messages.
Or create your own little client, using svcutil.exe:
svcutil.exe (service URL)
will create a little *.cs file and a *.config file for you, which you can then use to call the service.
Marc
You haven't given many details as to how far along you are with the service, so it's hard to say.
If this is literally the first hit to the service, this error could occur if WCF has not been registered properly with IIS. Specifically the .svc extension needs to be mapped to the ASP.NET ISAPI module.
thanks for taking the time out to answer this.
The service works fine, if a client creates a reference to my WCF Service and makes a method call, the appropriate response is sent.
I forgot to add, that my client is sends a HTTP Post Request to my WCF Service.
The appropriate response is then created and returned to the Client.
I can read the HTTP Request, however when i try and access the HTTP response, i get error -"The remote server returned an error: (400) Bad Request"
The error happens when the code reaches this line:
// Get the response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
See code below:
private void CreateMessage()
{
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create("http://www.XXXX.com/Feeds");
string postData = "<airport>Heathrow</airport>";
// user function
request.Method = "POST";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/soap+xml; charset=utf-8";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
// Get the response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
// Display the status.
HttpContext.Current.Response.Write(((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
HttpContext.Current.Response.Write(responseFromServer);
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
}
regards
Kojo
Note
The recommended way of accessing WCF Service from other .NET application is by using the "Connected Services" reference. Below I describe how you can create and send SOAP requests in a more manual (and not recommended for production code) manner.
In short
You need:
Content-Type: text/xml; charset=utf-8 header
SOAPAction: http://tempuri.org/YourServiceClass/YourAction header
Request content wrapped in SOAP envelope.
Longer version (example)
Lets take a WCF Service Application scaffolding as an example.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Using Wireshark, I found out that the requests made the default way (connected service reference) contain Content-Type: text/xml; charset=utf-8 and SOAPAction: http://tempuri.org/IService1/GetData headers and following SOAP envelope:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetData xmlns="http://tempuri.org/"> <!-- Action name -->
<value>123</value> <!-- Parameters -->
</GetData>
</s:Body>
</s:Envelope>
Using Insomnia, I tested that it's all we need in order to make the request pass successfully, so now just need to port it to the C#:
// netcoreapp3.1
static async Task<string> SendHttpRequest(string serviceUrl, int value)
{
// Example params:
// serviceUrl: "http://localhost:53045/Service1.svc"
// value: 123
using var client = new HttpClient();
var message = new HttpRequestMessage(HttpMethod.Post, serviceUrl);
message.Headers.Add("SOAPAction", "http://tempuri.org/IService1/GetData"); // url might need to be wrapped in ""
var requestContent = #$"
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<GetData xmlns=""http://tempuri.org/"">
<value>{value}</value>
</GetData>
</s:Body>
</s:Envelope>
";
message.Content = new StringContent(requestContent, System.Text.Encoding.UTF8, "text/xml");
var response = await client.SendAsync(message);
if (!response.IsSuccessStatusCode)
throw new Exception("Request failed.");
var responseContent = await response.Content.ReadAsStringAsync();
/*
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataResponse xmlns="http://tempuri.org/">
<GetDataResult>You entered: {value}</GetDataResult>
</GetDataResponse>
</s:Body>
</s:Envelope>
*/
// Just a really ugly regex
var regex = new Regex(#"(<GetDataResult>)(.*)(<\/GetDataResult>)");
var responseValue = regex.Match(responseContent).Groups[2].Value;
return responseValue;
}
You can ofc. use WebClient instead of HttpClient if preferred.