I have a WCF service that returns JSON to the clients.
This is the app.config
<system.serviceModel>
<services>
<service name="MyServices.MyService">
<endpoint address="" behaviorConfiguration="JsonBehavior" binding="webHttpBinding"
contract="Myervices.IMyervice">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/MyEmulator/Service1/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="JsonBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
This is a sample opertaion exposed by the service
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, Method = "GET", UriTemplate = "me/{id}")]
JsonObject GetObject(string id, string connection_type);
Now, I would like to log every request that the service receives from clients and response that is being sent back to clients...in this case it sin JSOn.
I have create a custom MessageInspector as follows
public class MyMessageInspector : IDispatchMessageInspector
{
private bool LoggingEnabled;
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
Guid correlationState = Guid.NewGuid();
if (this.LoggingEnabled)
{
StringBuilder message = new StringBuilder();
message.AppendFormat("CorrelationState = {0}{1}", correlationState, Environment.NewLine);
message.AppendFormat("Local Address = {0}{1}", channel.LocalAddress, Environment.NewLine);
message.AppendFormat("Remote Address = {0}{1}", channel.RemoteAddress, Environment.NewLine);
message.AppendLine("Request");
message.AppendLine(request.ToString());
message.AppendLine();
this.logger.LogMessage(message.ToString());
}
return correlationState;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (this.LoggingEnabled)
{
StringBuilder message = new StringBuilder();
message.AppendFormat("CorrelationState = {0}{1}", correlationState, Environment.NewLine);
message.AppendLine("Reply");
message.AppendLine(reply.ToString());
message.AppendLine();
this.logger.LogMessage(message.ToString());
}
}
#endregion
}
But this doesnt seem to log any of the resposne.. the response.Tostring() only says "...stream"
the Above works if my service returns data in SOAP XML format..but i want it to log JSON response which is the text inside the *.js file downloaded in the browser when i call the REST service endpoint. ex: http://localhost:8080/MyService/me/2/
any ideas how I can achieve this?
This code provides a mechanism for working with REST messages, and as a bonus allows SOAP to pass through as normal. It's a little intrusive (the stream is destroyed by reading so the code needs to regenerate it) but I haven't found any better way.
http://code.msdn.microsoft.com/WCF-REST-Message-Inspector-c4b6790b
Related
Client side Code
I have created web application and added service reference to the Client Project
I tried creating a client Object like this:
Service1Client a = new Service1Client();
but getting an error message :
Could not find default endpoint element that references contract 'ServiceReference_test.IService1' in the ServiceModel client
configuration section. This might be because no configuration file was
found for your application, or because no endpoint element matching
this contract could be found in the client element.
Could you please let me know what mistake am I doing, I am new to WCF please help me
WCF service which returns JSON Format:
namespace WcfService1
{
public class Service1 : IService1
{
public string GetData(string Id)
{
return ("You entered: " + Id);
}
}
}
namespace WcfService1
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/GetData/{Id}",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped
)]
string GetData(string Id);
// TODO: Add your service operations here
}
}
Web.Config file
<system.serviceModel>
<services>
<service name="WcfService1.Service1" behaviorConfiguration="ServiceBehaviour">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" contract="WcfService1.IService1"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp helpEnabled="true" automaticFormatSelectionEnabled="false" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<!--<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>-->
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
You should add following tags in your client App.config or Web.config:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="WebHttpBinding" />
</webHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8733/Design_Time_Addresses/fine_service/FineService/soap"
binding="webHttpBinding" bindingConfiguration="WebHttpBinding"
contract="ServiceReference1.IService1" name="WebHttpBinding" />
</client>
</system.serviceModel>
if you have this tags in your cofig file then make sure that client endpoint contract name should be the same as it is in your service endpoint. In your case contract name is IService1
EDIT:
also see this
ILeaveManagement class
[ServiceContract]
public interface ILeaveManagement
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "get")]
List<ServiceReference1.LeaveRequest> GetLeaveDetails();
}
LeaveManagement class
public class LeaveManagement : ILeaveManagement
{
public List<ServiceReference1.LeaveRequest> GetLeaveDetails()
{
try
{
var entities = new ServiceReference1.leaverequest_Entities(new Uri(serviceUrl));
var result = entities.LeaveRequestCollection;
return result.ToList();
}
catch
{
return new List<ServiceReference1.LeaveRequest>();
}
}
}
configuration
<service behaviorConfiguration="DRLExternalList.LeaveManagementBehavior" name="DRLExternalList.LeaveManagement">
<endpoint address="" binding="wsHttpBinding" contract="DRLExternalList.ILeaveManagement"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
<behavior name="DRLExternalList.LeaveManagementBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
I have deployed the project in IIS 7.5. When i run the application , it is saying BadRequest.
I have verrified in fiddler. i saw 400 error.
Please help me on this.
Try using webHttpBinding in your endpoint instead of the wsHttpBinding, or add it as an additional one and change the address. I use a bindingNamespace in my project, but I don't think you need it.
<endpoint address="XMLService"
binding="webHttpBinding"
behaviorConfiguration="restXMLBehavior"
contract="DRLExternalList.ILeaveManagement">
</endpoint>
Add an Endpoint Behavior
<endpointBehaviors>
<!-- Behavior for the REST endpoint -->
<behavior name="restXMLBehavior">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
I also annotate the OperationContract slightly differently, but it shouldn't make all that much of a difference. I'll give it to you just in case...
[WebGet(UriTemplate = "/GetLeaveDetails", ResponseFormat = WebMessageFormat.Xml)]
To call the service, it would look like this using the XMLService endpoint name:
http://myWebHost.com/WebService/MyService.svc/XMLService/GetLeaveDetails
Hosting an wcf service into a website issue : System.ArgumentException: ServiceHost only supports class service types
the above link helped me to solve my issue.
<%# ServiceHost Language="C#" Debug="true" Service="restleave.ProductRESTService" %>
I came across a page on MSDN explaining transaction in WCF Services here. I tweaked the binding settings and used netTcpBinding. Here is the serviceModel section of my app.config file:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpBindingConfiguration1" transactionFlow="true">
<security mode="Message" />
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="OrderingService.OrderService">
<clear />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"
listenUriMode="Explicit">
</endpoint>
<endpoint address="net.tcp://localhost:8880/OrderingService"
binding="netTcpBinding" bindingConfiguration="netTcpBindingConfiguration1"
contract="OrderingService.IOrderService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8888/OrderingService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
I created a windows application as the client of the service. I used netstat command to see the TCP connections between the client and the service (hosted in a console application). I realized for each operation (which was a button click in my client app that places a new order by invoking the methods of the service's proxy class), a new connection is created and all previous connections still remain ESTABLISHED. Obviously, this is not an ideal condition. I wondered what I did wrong and what setting or configuration would work out this problem by reducing the number of connections to only one. By the way, the service class that implements the service interface has InstanceContextMode set to PerSession. Here are the contract interface and the service class:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IOrderService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Customer> GetCustomers();
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Product> GetProducts();
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string PlaceOrder(Order order);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustInventory(int productId, int quantity);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustBalance(int customerId, decimal amount);
}
[ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Serializable,
TransactionTimeout = "00:00:20",
InstanceContextMode = InstanceContextMode.PerSession,
TransactionAutoCompleteOnSessionClose = true)]
public class OrderService : IOrderService
{...}
Here is the code the uses the proxy class in the client app:
using (TransactionScope scope = new TransactionScope())
{
try
{
proxy = new OrderServiceClient("NetTcpBinding_IOrderService");
result = proxy.PlaceOrder(order);
MessageBox.Show(result);
result = proxy.AdjustInventory(product.ProductId, quantity);
MessageBox.Show(result);
result = proxy.AdjustBalance(customer.CustomerId, product.Price * quantity);
MessageBox.Show(result);
proxy.Close();
scope.Complete();
}
catch (Exception exc)
{
MessageBox.Show("Error occurred: " + exc.Message);
}
}
With regards to the TCP connection remaining ESTABLISHED - are you calling .Close() on your instance of the client when you are finished with it?
If you want to use a single connection you should change the instance context mode to 'Single' and reuse the connection you establish in the client to process all your service calls. This suits an architecture where you want to maintain state within your service.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IService
{
}
I found this link very helpful when I was learning about context modes in WCF: CodeProject link
As you are currently using PerSession context mode you should be able to limit it to a single connection by adding a setting for maxConcurrentSessions in your behaviors section. You can do it like this:
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceThrottling maxConcurrentSessions="1" />
</behavior>
</serviceBehaviors>
This would only be a good idea if you have a single client.
I have a simple WCF service defined by the following ServiceContract:
[ServiceContract]
public interface IInventoryService
{
[OperationContract]
Item GetItemFromBarcode(string barcode);
[OperationContract]
string Test(string testString);
}
With Item defined like this:
[DataContract]
public class Item
{
[DataMember]
public virtual int Id { get; set; }
<Snip>
}
And the actual service implemented thusly:
public class InventoryService : IInventoryService
{
[WebGet(UriTemplate = "/Barcode/{barcode}/Item", ResponseFormat = WebMessageFormat.Json)]
public Item GetItemFromBarcode(string barcode)
{
var item = (from b in repository.Query<ItemBarcode>()
where b.BarcodeData == barcode
select b.Item).FirstOrDefault();
return item;
}
[WebGet(UriTemplate = "/Test/{testString}",ResponseFormat=WebMessageFormat.Xml)]
public string Test(string testString)
{
return testString;
}
}
With the following in the app.config for the program hosting the service:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RESTFriendly">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="InventoryService">
<endpoint address="/Inventory" behaviorConfiguration="RESTFriendly" binding="webHttpBinding" contract="IInventoryService"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
Now that the code dump is out of the way, the issue: I can invoke the Test method just fine with curl or Fiddler, it returns a serialized string. However, invoking the method that returns an object returns nothing. Curl spits back curl: (56) Failure when receiving data from the peer and Fiddler responds ReadResponse() failed: The server did not return a response for this request.
From what I read, this should Just Work (tm). Is there something obvious I'm missing?
So, it seems you can't have DataMember of an interface type, like the IList<ItemBarcode>. There go my hopes of using my NHibernate model objects as DTOs.
I'm trying to process a post by a third party server (Paypal) processed by my server through a WCF (.Net 3.5 SP1) service. All I need is to get and process the values with the query string. This sounds incredibly easy, but after a weekend, I'm still stumped. Any help would be greatly appreciated.
Passing the following URL from my browser to the service causes a AddressFilter mismatch fault (below).
http://localhost:9409/PPInfo.svc/ReadResponse?RESULT=0&AUTHCODE=10001&RESPMSG=APROVED&PNREF=12345
produces
<Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
- <Code>
<Value>Sender</Value>
- <Subcode>
<Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:DestinationUnreachable</Value>
</Subcode>
</Code>
- <Reason>
<Text xml:lang="en-US">The message with To 'http://localhost:9409/PPInfo.svc/ReadResponse?RESULT=0&AUTHCODE=10001&RESPMSG=APROVED&PNREF=12345' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree.</Text>
</Reason>
</Fault>
// web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ASEEESPrivate.PPInfoBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ASEEESPrivate.PPInfoBehavior" name="ASEEESPrivate.PPInfo">
<endpoint address="" binding="webHttpBinding" contract="ASEEESPrivate.IPPInfo">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
================================
[ServiceContract]
public interface IPPInfo
{
// expecting RESULT = 0 and RESPMSG = APPROVED
[OperationContract]
[WebGet(UriTemplate = "Presponse?RESULT={result}&AUTHCODE={authcode}&RESPMSG={respmsg}&AVSDATA={avsdata}&PNREF={pnref}",
BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml)]
void ReadResponse();
}
=========================================
// write parameters of query string to tlkpOnlineMessage table
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, AddressFilterMode=AddressFilterMode.Any)]
public class PPInfo : IPPInfo
{
public void ReadResponse()
{
var qString = HttpContext.Current.Request.QueryString;
var ctx =
new MembershipEntities();
var log = new tlkpOnlineMessage();
foreach (string s in qString)
{
log.LoginPageMsg = s;
ctx.AddTotlkpOnlineMessages(log);
}
ctx.SaveChanges();
}
}
Besides the typo in your code, I believe you have two problems that are easily correctable:
1) For a RESTful service, you should define an endpoint behavior and enable the webHttp behavior. You can do this by adding an <endpointBehavior> to your web.config as such:
<behaviors>
<serviceBehaviors>
<behavior name="ASEEESPrivate.PPInfoBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="ASEEESPrivate.PPInfoEndpointBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
And then add this endpoint behavior to your service definition:
<service behaviorConfiguration="ASEEESPrivate.PPInfoBehavior" name="WcfRestService1.PPInfo">
<endpoint address="" binding="webHttpBinding" contract="WcfRestService1.IPPInfo"
behaviorConfiguration="ASEEESPrivate.PPInfoEndpointBehavior">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
2) Secondly, you define a UriTemplate for your service with placeholders (result, authcode, etc.) but you don't define parameters for them in your interface. If you're going to define a UriTemplate for your service definition with placeholders, then you need to have your service define those parameters accordingly. You can do that as such:
[ServiceContract]
public interface IPPInfo
{
// expecting RESULT = 0 and RESPMSG = APPROVED
[OperationContract]
[WebGet(UriTemplate = "ReadResponse?RESULT={result}&AUTHCODE={authcode}&RESPMSG={respmsg}&AVSDATA={avsdata}&PNREF={pnref}",
BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml)]
void ReadResponse(string result, string authcode, string respmsg, string avsdata, string pnref);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, AddressFilterMode = AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PPInfo : IPPInfo
{
public void ReadResponse(string result, string authcode, string respmsg, string avsdata, string pnref)
{
...
}
}
By using a UriTemplate, you don't need to extract your querystring parameters out; or, if you want to extract parameters from your querystring, don't define a UriTemplate.
Once I made these two changes, I was able to get the service to run locally on my machine.
I hope this helps!