views:

334

answers:

5

I have a trivial .Net 2.0 SOAP web service. I want to access it from Silverlight application that is hosted on the same server, but different port. I know that for this to work I need to provide a clientaccesspolicy.xml or crossdomain.xml policy file (the service is available at http://example:8085/RemoteObject.rem?wsdl , so the policy file has to be present at http://example:8085/crossdomain.xml). What should I add to the following simple web service to self-serve the policy file like the WCF example does?

The web service is being run on Mono, although that shouldn't change anything - there's just no IIS involved.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

namespace ConsoleApplication1
{
    class RemoteObject : MarshalByRefObject
    {
     static void Main()
     {
      var channel = new HttpChannel(8085);
      ChannelServices.RegisterChannel(channel, false);

      RemotingConfiguration.RegisterWellKnownServiceType(
       typeof(RemoteObject), "RemoteObject.rem",
       WellKnownObjectMode.Singleton);

      Console.WriteLine("Press ENTER to exit the server.");
      Console.ReadLine();
     }

     public DateTime Now()
     {
      return DateTime.Now;
     }
    }
}

EDIT: because of all the unusable answers, let me repeat myself: I need to do this with .Net 2.0, not 3.0 or 3.5. WCF is not available.

A: 

Does this help?

http://www.dotnetcurry.com/ShowArticle.aspx?ID=208&AspxAutoDetectCookieSupport=1

The example you linked is WCF and .Net 3.5 - it's just a more elaborate code for the same thing I linked. It does what I need, but not with .Net 2.0.
skolima
A: 

Take a look at this http://blogs.msdn.com/carlosfigueira/archive/2008/03/07/enabling-cross-domain-calls-for-silverlight-apps-on-self-hosted-web-services.aspx

Tim Heuer
WCF => .Net 3.0 at least. I know this is trivial with WCF, I just don't have the luxury of using it.
skolima
+2  A: 

Hi,

I dont know much about the deploymnets in MONO. I would suggest a different approach if you didnt find any better approaches for your question.

Instead of directly calling the webservice from silverlight app.. you can invoke a javascript method from your managed silverlight code using

string sJson = HtmlPage.Window.Invoke("JSMethod", new string[] { strInParam }) as string;

and fire a AJAX request (from JS method) to your server and will internally make a call to the webservice deployed in MONO (from server) and return a JSON formatted result.

I have implemented this approach in my projects and its working fine..

just an alternative..

Cheers

Ramesh Vel

Ramesh Vel
This looks like an excellent answer
JustLoren
thanks Loren...
Ramesh Vel
+1  A: 

Ok, I can say that answer is not an easy step. Please look at Cassini open source web server, and you will have to implement a small web server in your application and run your custom service in your custom web server.

And the best way to open this silverlight would be, create an IFrame and let it load the html/aspx from your custom web server from your custom port itself. So you would not need any cross domain policy problems.

Akash Kava
A: 

EDIT2: my coworker has found a usable solution: Web Service Enhancements from Microsoft. It does need IIS and it has been deprecated with the introduction of WCF, but works well with plain .Net Framework 2.0 and should be deployable with Mono XSP.

EDIT: solution below is pointless, because .Net 2.0 exposes web services using SOAP 1.1 rpc/encoded model, and Silverlight requires SOAP 1.2 document/literal. So while the workaround works for the problem indicated in the question, the web service still cannot be consumed.

I managed to make this work without resorting to extreme hacks. The key to my solution was to insert an additional IServerChannelSink into the request processing queue. So, I changed

var channel = new HttpChannel(8085);

to register my custom IServerChannelSink before the normal pipeline:

var provider = ChainProviders(
  new PolicyServerSinkProvider(),
  new SdlChannelSinkProvider(),
  new SoapServerFormatterSinkProvider(),
  new BinaryServerFormatterSinkProvider());
var channel = new HttpChannel(new Hashtable(1) {{"port", 8085}}, null, provider);

I use a helper method to chain the sink providers together:

private static IServerChannelSinkProvider ChainProviders(
  params IServerChannelSinkProvider[] providers)
{
  for (int i = 1; i < providers.Length; i++)
    providers[i-1].Next = providers[i];
  return providers[0];
}

PolicyServerSinkProvider simply creates a PolicyServerSink:

internal class PolicyServerSinkProvider : IServerChannelSinkProvider
{
  public void GetChannelData(IChannelDataStore channelData){}

  public IServerChannelSink CreateSink(IChannelReceiver channel)
  {
    IServerChannelSink nextSink = null;
    if (Next != null)
      nextSink = Next.CreateSink(channel);
    return new PolicyServerSink(channel, nextSink);
  }

  public IServerChannelSinkProvider Next { get; set; }
}

PolicyServerSink delegates all messages down the chain, except when it gets a request for crossdomain.xml - then it writes the needed xml into the response stream.

internal class PolicyServerSink : IServerChannelSink
{
  public PolicyServerSink(
    IChannelReceiver receiver, IServerChannelSink nextSink)
  {
    NextChannelSink = nextSink;
  }

  public IDictionary Properties { get; private set; }

  public ServerProcessing ProcessMessage(
    IServerChannelSinkStack sinkStack, IMessage requestMsg,
    ITransportHeaders requestHeaders, Stream requestStream,
    out IMessage responseMsg, out ITransportHeaders responseHeaders,
    out Stream responseStream)
  {
    if (requestMsg != null || ! ShouldIntercept(requestHeaders))
      return NextChannelSink.ProcessMessage(
        sinkStack, requestMsg, requestHeaders, requestStream,
        out responseMsg, out responseHeaders, out responseStream);

    responseHeaders = new TransportHeaders();
    responseHeaders["Content-Type"] = "text/xml";
    responseStream = new MemoryStream(Encoding.UTF8.GetBytes(
      @"<?xml version=""1.0""?><!DOCTYPE cross-domain-policy SYSTEM "
      + @"""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd""&gt;"
      + @"<cross-domain-policy><allow-access-from domain=""*"" />"
      + @"</cross-domain-policy>")) {Position = 0};
    responseMsg = null;
    return ServerProcessing.Complete;
  }

  private static bool ShouldIntercept(ITransportHeaders headers)
  {
    return ((string) headers["__RequestUri"]).Equals(
      "/crossdomain.xml", StringComparison.InvariantCultureIgnoreCase);
  }

  public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack,
    object state, IMessage msg, ITransportHeaders headers, Stream stream)
  {
  }

  public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack,
    object state, IMessage msg, ITransportHeaders headers)
  {
    throw new NotSupportedException();
  }

  public IServerChannelSink NextChannelSink { get; private set; }
}

This can also be used to serve other files together with the web service. I am currently using this method to host my Silverlight application (the consumer of the web service) without a separate http server.

skolima
.NET 2.0 can also do document/literal.
John Saunders