I want to get a meaningful error message from my WCF service for my Silverlight 4 application. After some investigation, I found that I need to change the reply code from 500 to 200 if I want silverlight enable to read the meaningful error message. Here is the article: http://msdn.microsoft.com/de-de/library/ee844556(VS.95).aspx
I have implemented it as it is written there, the application compiles and I can use the service - but I still get the 500 return code. The main difference I see is that I call the service via HTTPS not HTTP. Maybe this is the reason, why it doesn't work? Any idea, how to get the return code 200?
Here is my Web.Config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ServiceConfiguratorDataSource.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="silverlightFaults" type="ServiceConfiguratorDataSource.SilverlightFaultBehavior, ServiceConfiguratorDataSource, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<services>
<service name="ServiceConfiguratorDataSource.Service" behaviorConfiguration="ServiceConfiguratorDataSourceBehaviour">
<endpoint address="" binding="customBinding" behaviorConfiguration="SLFaultBehavior" bindingConfiguration="ServiceConfiguratorCustomBinding" contract="ServiceConfiguratorDataSource.IService" />
</service>
</services>
<bindings>
<customBinding>
<binding name="ServiceConfiguratorCustomBinding">
<security authenticationMode="UserNameOverTransport"></security>
<binaryMessageEncoding></binaryMessageEncoding>
<httpsTransport/>
</binding>
</customBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceConfiguratorDataSourceBehaviour">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="ServiceConfiguratorDataSource.UserCredentialsValidator,ServiceConfiguratorDataSource" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="SLFaultBehavior">
<silverlightFaults/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
... and here the silverlightFaultBehavior.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel;
namespace ServiceConfiguratorDataSource
{
public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
public void BeforeSendReply(ref Message reply, object correlationState)
{
if (reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
// Here the response code is changed to 200.
property.StatusCode = System.Net.HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// Do nothing to the incoming message.
return null;
}
}
// The following methods are stubs and not relevant.
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public override System.Type BehaviorType
{
get { return typeof(SilverlightFaultBehavior); }
}
protected override object CreateBehavior()
{
return new SilverlightFaultBehavior();
}
}
}
Someone knows if this is because of https ... and if so, how to get it to work?
Thanks in advance,
Frank
EDITH says: I just have added some logging: the ApplyDispatchBehavior - method is called, but the BeforeSendReply - method not ... any ideas why?
If I remember correctly, the UserNamePasswordValidator gets called very early in the pipeline, before the dispatcher ever gets called, which is why your custom dispatch behavior isn't affecting anything. (The reason is security: WCF wants to "throw out" unauthorized requests as early as possible, while running as little code as possible for them).
As you yourself suggested in the comments, one solution would be to just validate the credentials later in the pipeline - e.g. in every operation (or maybe even in a message inspector's AfterReceiveRequest?)
Related
In a very simple Silverlight Application I have a DomainService Class which has a single method that returns a list of Letter Objects.
The application works fine when I run it in VisualStudio. However, when I publish it to a folder on my Windows 10 local machine and run it using IIS (version 10.0.166299.5) I get the following error:
The remote server returned an error: NotFound.
at System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception error) at System.ServiceModel.DomainServices.Client.LoadOperation.Complete(Exception error) at System.ServiceModel.DomainServices.Client.DomainContext.CompleteLoad(IAsyncResult asyncResult) at System.ServiceModel.DomainServices.Client.DomainContext.<>c__DisplayClass1b.b__17(Object )
I supect this is due to something being wrong in missing in my WebConfig file. My WebConfig Currently looks like this:
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.6" />
<httpRuntime targetFramework="4.6" />
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="DomainServiceModule" preCondition="managedHandler" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</modules>
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
The code for my Domain Service Class is like this:
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using SilverData.Web.Models;
namespace SilverData.Web.Services
{
[EnableClientAccess]
public class DrugsRiaService : DomainService
{
public IQueryable<Letter> GetAllLetters()
{
List<Letter> letters = new List<Letter>();
Letter letterA = new Letter { ID = 1, Statement = "Mike" };
Letter LetterB = new Letter { ID = 2, Statement = "Emma" };
Letter LetterC = new Letter { ID = 3, Statement = "Peter" };
letters.Add(letterA);
letters.Add(LetterB);
letters.Add(LetterC);
return letters.AsQueryable();
}
}
}
The error was due to the problem that .svc file wasn't being served.. The problem got solved with the kind help from the Kyle Abraham on Experts Exchange.
https://www.experts-exchange.com/questions/29084691/RIA-Service-Error-in-a-Silverlight-Application.html
The solution was to add the following line to the webserver section of the Webconfig
<handlers>
<add name=".svc" verb="*" path="*.svc" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</handlers>
I'm not sure, its a guess as I have not used RIA for awhile, but I think letters needs to returned as something other than queryable... try ToList() which causes the query to execute, and the payload is complete with the complete enumeration that was retrieved from the database. Remember, this is a remote call from a client, not a local one that can extend the queryable.
It seems like I've been banging my head with custom faults in my Silverlight WCF service forever so I will happily DO MY BEST TO BUY A BEER for anyone who can help me solve this!!!
After much pain I finally have my WCF service throwing custom errors (ParameterValidationFault) and using Fiddler I know that the service's response contains my serialized fault object, but the HTTP response code is 500, not 200, so the client starts throwing exceptions rather than reading the response.
I know my SilverlightFaultBehavior class is supposed to take care of changing the response status code, but the breakpoints I set there are never being hit, so I'm hoping this is a simple web.config issue (web.config at end of post).
If this is relevant my web.config shows "the element 'behavior' has invalid child element 'silverlightFaults'...", in the section
<endpointBehaviors>
<behavior name="SilverlightFaultBehavior">
<silverlightFaults/>
</behavior>
</endpointBehaviors>
but I thought this wasn't a problem as I can view the service's metadata without error. However now I'm thinking this is the missing link that's preventing my status code from being changed on the way out. I have read that this error indicates a problem with the type attribute within my behaviorExtension element not exactly matching what .NET thinks it should be, but I have checked this a million times and the namespace and assembly name are definitely correct. I haven't messed with the version, culture, or public key stuff.
Is there a simple way for .NET to tell me exactly what this type string should be (spaces, commas, and all)? I have viewed the dll's properties in explorer but I'm still no closer.
Any other suggestions on where this might be coming from would be hugely appreciated.
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="silverlightFaults" type="my.namespace.SilverlightFaultBehavior, AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="SilverlightFaultBehavior">
<silverlightFaults/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="my.namespace.IService.customBinding0">
<binaryMessageEncoding />
<httpTransport />
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="my.namespace.IService">
<endpoint address="" binding="customBinding" bindingConfiguration="my.namespace.IService.customBinding0" contract="my.namespace.IService" behaviorConfiguration="SilverlightFaultBehavior" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
My SilverlightFaultBehavior class starts like this and is a copy-paste of this MSDN post with a namespace change
namespace my.namespace
{
public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
OK so this problem has not been resolved but worked-around. I finally saw in this very useful post that I can have anonymous endpointBehaviors. The behaviour is now applied and custom faults are correctly (in a non-standard kind of way) returned as HTTP 200s. Making the behavior anonymous means it is applied to all endpoints, but as my service currently only needs a single endpoint this works for me.
It truly sucks that I had all this grief after reading the friggin manual and implementing it word for word. In the end my config parse error "the element 'behavior' has invalid child element 'silverlightFaults'..." was irrelevant, but a very smelly red-herring along the way as it very well could have been the cause (and still may be).
In case anyone is wondering, I just drank the beer.
My Silverlight/WCF application uses PrincipalPermission in each service method to ensure the user is Authenticated. This works just fine when I have everything configured to HTTP, but once I configured my service endpoints/bindings to support HTTPS (SSL), I get an exception thrown when I call the Demand() method of my PrincipalPermission object.
EDIT: I should mention I am using IIS 7.5 Express to host and debug this project.
Here is the method that checks to make sure the user is authendicated. It's called from each of my service methods:
protected void SecurityCheck(string roleName, bool authenticated)
{
System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
PrincipalPermission p = new PrincipalPermission(null, roleName, authenticated);
try
{
p.Demand();
}
catch (Exception ex)
{
/* wrap the exception so that Silverlight can consume it */
ServiceException fault = new ServiceException()
{
/* Code = 1 will mean "unauthenticated!" */
Code = 1, Message = ex.Message
};
throw new FaultException<ServiceException>(fault); }
}
}
The execption thown is "Request for principal failed."
Here are the important bits of my web.config file:
<behavior name="BinarySSL">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="6553600"/>
<serviceTimeouts transactionTimeout="00:10:00"/>
</behavior>
<binding name="MyApp.Web.Services.ProjectService.customBinding0"
receiveTimeout="00:10:00" sendTimeout="00:10:00">
<binaryMessageEncoding />
<httpsTransport authenticationScheme="Basic"/>
</binding>
<service name="MyApp.Web.Services.ProjectService" behaviorConfiguration="BinarySSL">
<endpoint address="" binding="customBinding" bindingConfiguration="MyApp.Web.Services.ProjectService.customBinding0"
contract="MyApp.Web.Services.ProjectService" />
</service>
Here is the ClientConfig:
<binding name="CustomBinding_ProjectService">
<binaryMessageEncoding />
<httpsTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
<endpoint address="https://localhost:44300/Services/ProjectService.svc"
binding="customBinding" bindingConfiguration="CustomBinding_ProjectService"
contract="ProjectProxy.ProjectService" name="CustomBinding_ProjectService" />
I'm hoping someone can point in in the right direction here. Again, this configuration works just fine until I configure my services for SSL. Any thoughts?
Thanks,
-Scott
I thought I found the problem, and answered my own question - but I was wrong. Still have the same issue.
I have a Silverlight app that consumes a WCF service in my asp.net application.The silverlight app saves "drawings" in the form of ink strokes. These strokes are in the form of xaml (which can be kind of large) of which I am converting to a string and sending to the service to save the string to sql server. (Basically does the same type of thing as this app http://msdn.microsoft.com/en-us/magazine/cc721604.aspx).
This works and I can see my service method is being hit when the drawing is relatively small, but if the drawing gets to big I get this great error and the services method break point never gets hit. It seems that I am passing a size threshold, but I can't figure out if I'm right or what to change to resolve the issue.
I have seached google and SO with no success, so any help would be appreciated. Thanks in advance.
My Service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class DrawingService: IDrawingService
{
#region IDrawingService Members
public bool SaveEvalAreaDrawing(int EvalAreaId, string xmlToSave)
{
return true;
}
public bool SaveEvalItemDrawing(int EvalItemId, string xmlToSave)
{
return true;
}
public string GetEvalAreaDrawing(int EvalAreaId, string xmlToSave)
{
return "you got the eval drawing!";
}
public string GetEvalItemDrawing(int EvalItemId, string xmlToSave)
{
return "you got the eval item drawing!";
}
#endregion
}
My asp.net app with service web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyNameSpace.Services.DrawingServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="customBinding0">
<binaryMessageEncoding />
<httpTransport>
<extendedProtectionPolicy policyEnforcement="Never" />
</httpTransport>
</binding>
</customBinding>
<wsHttpBinding>
<binding name="wsPlainBinding" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None">
<transport clientCredentialType="None">
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="None" negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service behaviorConfiguration="HSD.ECERS.Services.DrawingServiceBehavior"
name="HSD.ECERS.Services.DrawingService">
<endpoint address="Services" binding="wsHttpBinding" bindingConfiguration="wsPlainBinding"
name="wsPlainHttp" contract="HSD.ECERS.Services.IDrawingService" />
<endpoint address="mex" binding="mexHttpBinding" name="wsdl"
contract="IMetadataExchange" />
</service>
</services>
ServiceReferences.ClientConfig
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="wsPlainHttp">
<textMessageEncoding messageVersion="Default" writeEncoding="utf-8" />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<extendedProtectionPolicy policyEnforcement="Never" />
</httpTransport>
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:41594/Services/DrawingService.svc/Services"
binding="customBinding" bindingConfiguration="wsPlainHttp"
contract="EvalDrawingService.IDrawingService" name="wsPlainHttp" />
</client>
</system.serviceModel>
</configuration>
Where VS is showing the error
public bool EndSaveEvalAreaDrawing(System.IAsyncResult result) {
object[] _args = new object[0];
bool _result = ((bool)(base.EndInvoke("SaveEvalAreaDrawing", _args, result))); // Here is where is popping up
return _result;
}
Exception
{System.ServiceModel.CommunicationException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound.
at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
at System.Net.Browser.AsyncHelper.<>c__DisplayClass4.<BeginOnUI>b__1(Object sendState)
--- End of inner exception stack trace ---
at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
--- End of inner exception stack trace ---
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.DrawingServiceClientChannel.EndSaveEvalAreaDrawing(IAsyncResult result)
at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.EvaluaionAncillaryControl.EvalDrawingService.IDrawingService.EndSaveEvalAreaDrawing(IAsyncResult result)
at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.OnEndSaveEvalAreaDrawing(IAsyncResult result)
at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)}
Try increasing the readerQuotas -- add this element inside the binding element
<readerQuotas maxStringContentLength="2147483647" />
http://msdn.microsoft.com/en-us/library/ms731325.aspx
Have you tried turning on WCF Tracing? I'm not sure if that works with Silverlight (given that Silverlight has lots of security limitations in terms of the local harddrive), so you might need to set up a simple no silverlight client and reproduce the call with diagnostics turned on.
EDIT: Or, could be the maxArrayLength member of readerQuotas...
I have created a very basic service operation that needs to write content to my database. This service looks like the following:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = false)]
public class myService : ImyService
{
public MyServiceResult MyMethod(string p1, string p2)
{
try
{
// Do stuff
MyResponseObject r = new MyResponseObject();
r.Property1 = DateTime.Now;
r.Property2 = "Some other data";
return r;
}
catch (Exception ex)
{
return null;
}
}
}
ImyService is defined as shown here:
[ServiceContract]
public interface ImyService
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped)]
MyServiceResult MyMethod(string p1, string p2);
}
This service will be exposed to both WP7 and iPhone client applications. Because of this, I believe I need to use webHttpBinding. This has caused me to use the following settings in my web.config file:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="myServiceBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="myService">
<endpoint address=""
behaviorConfiguration="myServiceBehavior"
binding="webHttpBinding"
contract="ImyService" />
</service>
</services>
</system.serviceModel>
Both the service and WP7 app are part of the same solution. I can successfully add a reference to the service in my application. When I run the application though, the page that references the service throws an error. The error says:
Could not find default endpoint element that references contract 'MyServiceProxy.ImyService' 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.
What am I doing wrong? It just seems like this should be a pretty straightforward thing. Thank you for your help.
Have you copied the file "ServiceReferences.ClientConfig" into your Windows Phone 7 project? This file is in your WCF project. Also, WP7 clients support basicHttpBinding only. So, you may see an empty "ServiceReferences.ClientConfig" file unless you switch over to basicHttpBinding