SharePoint FBA & Reactive Extensions

Some of the dangers with new toys is to overuse them, I fear I might be doing that with reactive extensions but oh well.. It’s all about the learningSmile

Today I did a test to do Form Based Authentication with SharePoint using reactive extensions. This is the first attempt, it can probably be made more streamlined as I figure out more about Rx.

Oh, apologize for the syntax coloring in the code, I really need to find some better CSS for this…

I have two classes: One for doing the FBA (which also holds the cookie jar as we will get a cookie once the FBA is successful and need to pass this along to the next web service call) and one for doing web service calls.

public class FormsBasedAuthentication {
    public static CookieContainer CookieJar = new CookieContainer();
    private const string AuthEnvelope = @"<?xml version=""1.0"" encoding=""utf-8""?>
                <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
                  <soap:Body>
                    <Login xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                      <username>{0}</username>
                      <password>{1}</password>
                    </Login>
                  </soap:Body>
                </soap:Envelope>";

    public static IObservable<string> Authenticate(string server, string username, string password) {
        Uri authService = new Uri(string.Format("{0}/_vti_bin/authentication.asmx", server));
        HttpWebRequest authRequest = WebRequest.Create(authService) as HttpWebRequest;
        authRequest.CookieContainer = CookieJar;
        authRequest.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/Login";
        authRequest.ContentType = "text/xml; charset=utf-8";
        authRequest.Method = "POST";
        return (from request in Observable.FromAsyncPattern<Stream>(authRequest.BeginGetRequestStream, authRequest.EndGetRequestStream)().Do(stream => {
            UTF8Encoding encoding = new UTF8Encoding();
            string envelope = string.Format(AuthEnvelope, username, password);
            byte[] bytes = encoding.GetBytes(envelope);
            stream.Write(bytes, 0, bytes.Length);
            stream.Close();
        })
                from response in Observable.FromAsyncPattern<WebResponse>(authRequest.BeginGetResponse, authRequest.EndGetResponse)()
                select HandleResponse(response, authRequest));
    }

    private static string HandleResponse(WebResponse response, HttpWebRequest request) {
        var httpResponse = response as HttpWebResponse;
        if (httpResponse != null){
            var result = XDocument.Load(response.GetResponseStream());
            var code = result.Descendants(XName.Get("ErrorCode", "http://schemas.microsoft.com/sharepoint/soap/")).FirstOrDefault();
            if (code != null)
                return code.Value;
        }
        return "ErrorOccured";
    }

}

This will return an observable that will return the authentication result – if the login was successful, the published value will be NoError.

Next we have our web service class:

public class BaseWebService {
       private static string soapEnvelope =
           @"<soap:Envelope
                           xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
                           xmlns:xsd='http://www.w3.org/2001/XMLSchema'
                           xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
                   <soap:Body>{0}</soap:Body></soap:Envelope>";

       private XDocument soapEnvelopeXml;
       public string BaseUrl { get; set; }
       #region Helper methods
       private XDocument CreateSoapEnvelope(string content) {
           var envelope = string.Format(soapEnvelope, content);
           XDocument soapEnvelopeXml = XDocument.Parse(envelope);

           return soapEnvelopeXml;
       }

       private HttpWebRequest CreateWebRequest(string url, string action) {
           Uri serviceUrl = new Uri(url, UriKind.Absolute);
           var webRequest = WebRequest.Create(serviceUrl) as HttpWebRequest;
           webRequest.Headers["SOAPAction"] = action;
           webRequest.ContentType = "text/xml;charset=\"utf-8\"";
           webRequest.Accept = "text/xml";
           webRequest.Method = "POST";
           webRequest.CookieContainer = FormsBasedAuthentication.CookieJar;
           return webRequest;
       }
       #endregion

       public IObservable<XDocument> CallService<T>(string url, string action, string soapContent) {
           soapEnvelopeXml = CreateSoapEnvelope(soapContent);
           HttpWebRequest webRequest = CreateWebRequest(url, action);
           var call = (from request in Observable.FromAsyncPattern<Stream>(webRequest.BeginGetRequestStream, webRequest.EndGetRequestStream)().Do(stream => {
                                                                                                                                                       soapEnvelopeXml.Save(stream);
                                                                                                                                                       stream.Close();
                                                                                                                                                   })
                       from response in Observable.FromAsyncPattern<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse)()
                       select Handle(response));
           return call;
       }
       private XDocument Handle(WebResponse response){
           var doc = XDocument.Load(response.GetResponseStream());
           return doc;
       }
   }

This produces an observable which will publish a XDocument with the result of the web service call. As you can see, we are using our cookie jar from the FBA class (see the CreateWebRequest method) so that our authentication token gets passed along.

To put this together and do an authenticated call to the Lists.asmx web service in SharePoint, we need two small methods.

private static void GetList(string authStatus){
            if (authStatus == "NoError"){
                BaseWebService webService = new BaseWebService();
                webService.CallService<XDocument>(“<url>/_vti_bin/Lists.asmx”, ActionGetList, string.Format(SoapGetList, "Pages")).Subscribe(Handle);
            }
        }

This method gets the published value from the FBA class and checks that we can proceed with our next call.

Oh, must not forget the SoapAction and content of the envelope:

private const string ActionGetList = "http://schemas.microsoft.com/sharepoint/soap/GetList";
private const string SoapGetList = @"<GetList xmlns='http://schemas.microsoft.com/sharepoint/soap/' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'><listName>{0}</listName></GetList>";

 

private static void Handle(XDocument doc){
            Console.WriteLine(doc.ToString());
        }

And this one handles the result from the call to Lists.asmx (not doing much at this point).

So, to start it all off, we need to get our FBA observable set up:

var auth = FormsBasedAuthentication.Authenticate("<url to sharepoint site>", "<user>", "<password>");

Next we start it off:

auth.Do(GetList).Start();

What this does is that our FBA observable will call GetList for every published value that the FBA observable gives us (which should be just one) and to fire the entire process off we turn the ignition and .Start() Smile

This is all then done asynchronously.

Advertisements

Using reactive extensions with WebRequest

I’ve recently started looking at the Rx library from Microsoft labs and it’s very interesting stuff, even though I have to admit I have a hard time wrapping my head around it. But it starts to sink in, bit by bit 🙂


When working with Windows Phone 7 development and my SharePoint library for WP7, I do a lot of asynchronous calls to web services. This code can often get a bit messy with all the callback methods so I tried to refactor it using reactive extensions and I am pretty pleased with the result:) I’m no expert on the field so I won’t guarantee that this is the best way to do it, but it works.

Here’s a sample of how to do a call to the Lists.asmx web service in SharePoint:
string Envelope = @"<?xml version=""1.0"" encoding=""utf-8""?>
                                    <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
                                        <soap:Body>
                                            <GetList xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                                                <listName>{0}</listName>
                                            </GetList>
                                        </soap:Body>
                                    </soap:Envelope>";

        public void GetList(string server, string name, Action<XDocument> handler)
        {
            HttpWebRequest req = WebRequest.Create(string.Format("{0}/_vti_bin/lists.asmx", server)) as HttpWebRequest;
            req.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/GetList";
            req.ContentType = "text/xml; charset=utf-8";
            req.Method = "POST";
            var listRequest = (from request in Observable.FromAsyncPattern<Stream>(req.BeginGetRequestStream, req.EndGetRequestStream)().Do(stream =>
            {
                UTF8Encoding encoding = new UTF8Encoding();
                string e = string.Format(Envelope, name);
                byte[] bytes = encoding.GetBytes(e);
                stream.Write(bytes, 0, bytes.Length);
                stream.Close();
            })
                     from response in Observable.FromAsyncPattern<WebResponse>(req.BeginGetResponse, req.EndGetResponse)()
                     select HandleResponse(response)).Subscribe(handler);
        }
        private void DoStuff(XDocument xml)
        {
                //parse the xml result here
        }
        private XDocument HandleResponse(WebResponse response)
        {
            HttpWebResponse res = response as HttpWebResponse;
            if (res != null && res.StatusCode == HttpStatusCode.OK)
            {
                XDocument doc = XDocument.Load(response.GetResponseStream());
                return doc;
            }
            return null;
        }

Then of course we call the method:

GetList("http://spserver.com", "Pages", DoStuff);

In Windows Phone 7 we could add .ObserveOnDispatcher() before our .Subscribe() in the LINQ query so that the current Dispatcher is used when notifying observers.

We could also generalize the method so that it returns a IObservable<T> instead which we can subscribe to.

Transparent change tracking for properties

While working to develop a framework for communicating with SharePoint 2007 from Silverlight I found the need to have some way of tracking changes to property values without having to implement the same code in all properties.

Luckily, my colleague Einar Ingebrigtsen wrote a blog post about automagically implementing INotifyPropertyChanged which was perfect for this.
I’ll skip the details in this post as Einar’s post does this very well, instead I’ll focus on my usage of this technique.

The idea is that anyone using this framework can implement their own strong typed versions of an SPListItem so you can have code like this:

public virtual string Title { get; set; }

instead of having to specifically call various methods like this:

private string title;
public virtual string Title {
    get {
        return title;
    }
    set {
        SetPropertyValueDirty("Title");  //this marks the property as changed and stores its old value
        title = value;
        OnPropertyChanged("Title");  //implementation of INotifyPropertyChanged
    }
}

Being that these things are now handled automagically, you can focus on doing what you want in your code. The proxy class that is generated to do this will also respect your code, so while it will call these methods in the example above it will also run your code.

It also gives you the possibility of undoing changes to properties or resetting the entire object by reading from the storage you’ve implemented to hold the value changes (for example a dictionary) like so:

public object GetOldValueForProperty(string propertyName) {
    if (dirtyDictionary.ContainsKey(propertyName))
        return dirtyDictionary[propertyName];
    else
        throw new ArgumentException("Property does not have an old value");
}

Or just reset the property back to its original value:

public void ResetPropertyValue(string propertyName) {
    PropertyInfo pInfo = this.GetType().GetProperty(propertyName);
    if (pInfo != null) {
        pInfo.SetValue(this, GetOldValueForProperty(propertyName), null);
        CleanProperty(propertyName);
    }
}

As you see, the example above uses reflection to set the value. While reflection is an expensive operation, it is the only way I can use in the framework to set the value because I only have the name of the property in the case of classes implemented by a third party. If anyone has a better solution, I’m all ears 🙂

Another reason for needing this, is so I can known if a value needs to be written back to SharePoint or skipped.

As always, if anyone have any questions or suggestions for improvements – please let me know 🙂