Best Pattern for AllowUnsafeUpdates

前端 未结 6 1604
半阙折子戏
半阙折子戏 2020-12-28 08:54

So far, in my research I have seen that it is unwise to set AllowUnsafeUpdates on GET request operation to avoid cross site scripting. But, if it is required to allow this,

6条回答
  •  执念已碎
    2020-12-28 09:10

    I use a wrapper class for handling most manipulation of SPWeb objects. This helps me remember to close the web, and it eases the problems of unsafeupdates setting. It is a bit bloated, as I have patched on new constructors and members. but, then again; so is the SPWeb class.

    Usage:

    using (WebWrapper wrapper = new WebWrapper("http://localhost"))
                {
                    wrapper.AllowUnsafeUpdates();
    
                    //Do work on wrapper.
                }
    

    The class definition:

    using System;
    using System.Collections.Specialized;
    using System.Data;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Serialization;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    
    namespace Skaar.SharePoint.Customization
    {
        /// 
        /// A wrapper for a  object.
        /// Closes web object on Dispose if applicable.
        /// 
        [Serializable]
        [DebuggerDisplay("{Uri} Unsafe:{AllowUnsafeUpdatesSetting} Update:{UpdatePending}")]
        public sealed class WebWrapper : IDisposable, IDeserializationCallback, IEquatable
        {
            [NonSerialized] private bool unsafeUpdatesSetting;
    
            [NonSerialized] private SPWeb web;
    
            /// 
            /// Determines if the inner web object should be closed.
            /// 
            [NonSerialized] private bool webShouldBeClosed;
    
            /// 
            /// This value is used in serialization to restore .
            /// 
            private string webUrl;
    
            /// 
            /// Creates a new wrapper object.
            /// 
            /// A web that should be closed/disposed when done.
            public WebWrapper(SPWeb web) : this(web, true)
            {
            }
    
            /// 
            /// Creates a new wrapper object.
            /// 
            /// An inner web object
            /// If true, the web object is closed in the  method.
            public WebWrapper(SPWeb web, bool webShouldBeClosed)
            {
                setWeb(web, webShouldBeClosed);
            }
    
            /// 
            /// Creates a new wrapper object.
            /// 
            /// The address to a web.
            public WebWrapper(Uri webAddress)
            {
                using (SPSite site = new SPSite(webAddress.ToString()))
                {
                    string relativeUrl = renderWebRootRelativeUrl(webAddress);
                    if (relativeUrl == null)
                    {
                        setWeb(site.OpenWeb(), true);
                    }
                    else
                    {
                        setWeb(site.OpenWeb(relativeUrl), true);
                    }
                }
            }
    
            private string renderWebRootRelativeUrl(Uri address)
            {
                for (int i = 0; i < address.Segments.Length; i++)
                {
                    string segment = address.Segments[i];
                    if (string.Equals(segment, "_layouts/"))
                    {
                        string newUrl=string.Join(null, address.Segments, 0, i).Trim('/');
                        return newUrl;
                    }
                }
                return null;
            }
    
            /// 
            /// If true,  will be called in .
            /// 
            public bool UpdatePending { get; private set; }
    
            /// 
            /// The setting of the inner web ()
            /// 
            public bool AllowUnsafeUpdatesSetting
            {
                get { return Web.AllowUnsafeUpdates; }
            }
    
            /// 
            /// The inner object.
            /// 
            /// Exception is thrown if  is true.
            public SPWeb Web
            {
                get
                {
                    if(IsDisposed)
                    {
                        throw new ObjectDisposedException("Web wrapper is disposed.");
                    }
                    return web;
                }
            }
    
            /// 
            /// The address of the  wrapped as a  object.
            /// 
            public Uri Uri
            {
                get { return new Uri(Web.Url); }
            }
    
            /// 
            /// The address of the  wrapped as a  object.
            /// 
            public Uri GetUri(SPUrlZone zone)
            {
                return Site.WebApplication.GetResponseUri(zone, Uri.AbsolutePath);
            }
    
            /// 
            /// Creates a wrapper around the context web.
            /// The web will not be closed when wrapper is disposed. Returns null if context is unavailable.
            /// 
            public static WebWrapper Context
            {
                get
                {
                    return SPContext.Current==null?null:new WebWrapper(SPContext.Current.Web, false);
                }
            }
    
            /// 
            /// This is a static property wrapping of
            /// the  method, using
            /// the  current web as
            /// parameter.
            /// Returns null if context is unavailable.
            /// 
            public static WebWrapper CloneOfContext
            {
                get
                {
                    if (SPContext.Current != null)
                    {
                        SPWeb contextWeb = SPContext.Current.Web;
                        return CloneOf(contextWeb);
                    }
                    return null;
                }
            }
    
            /// 
            /// Returns the  property of the  object.
            /// 
            public bool Exists
            {
                get { return Web != null && Web.Exists; }
            }
    
            /// 
            /// Gets the  object of .
            /// 
            /// This object should not be closed by user code.
            public SPSite Site
            {
                get { return web.Site; }
            }
    
            /// 
            /// Gets the owner defined in .
            /// 
            public SPUser Owner
            {
                get
                {
                    return Site.Owner;
                }
            }
    
            /// 
            /// Returns a context of the inner .
            /// 
            public SPContext ContextOfWeb
            {
                get { return SPContext.GetContext(web); }
            }
    
            /// 
            /// Gets the language of .
            /// 
            public CultureInfo Locale
            {
                get { return Web.Locale; }
            }
    
            /// 
            /// Gets the language of the root web.
            /// 
            public CultureInfo LocaleOfRoot
            {
                get
                {
                    using (WebWrapper root = Root)
                    {
                        return root.Locale;
                    }
                }
            }
    
            /// 
            /// Returns a new  wrapping the root  of this.
            /// 
            public WebWrapper Root
            {
                get
                {
                    if (webShouldBeClosed)
                        using (SPSite site = Site)
                        {
                            return new WebWrapper(site.RootWeb);
                        }
                    return new WebWrapper(Site.RootWeb);
                }
            }
    
            /// 
            /// A wrapper for .
            /// 
            public string Title
            {
                get { return Web.Title; }
                set { Web.Title = value; }
            }
    
            /// 
            /// A wrapper for .
            /// 
            public Guid ID
            {
                get { return Web.ID; }
            }
    
            #region Web Properties
    
            [NonSerialized] private bool updatePropertiesPending;
    
            /// 
            /// A wrapper method to  object's  indexer.
            /// 
            /// The key to use when fetching property value.
            /// A string containing the value.
            public string GetProperty(string key)
            {
                return Web.Properties[key];
            }
    
            /// 
            /// Sets the value in the  object's . Creates a new key, or updates an existing as needed.
            /// 
            /// The key to use when storing the property value.
            /// The value to set in the key.
            /// The property  is set to true.
            public void SetProperty(string key, string value)
            {
                if (!Web.Properties.ContainsKey(key))
                {
                    Web.Properties.Add(key, value);
                }
                else
                {
                    Web.Properties[key] = value;
                }
                updatePropertiesPending = true;
            }
    
            #endregion
    
            #region IDeserializationCallback Members
    
            ///
            ///Runs when the entire object graph has been deserialized.
            ///
            ///
            ///The object that initiated the callback. The functionality for this parameter is not currently implemented. 
            public void OnDeserialization(object sender)
            {
                using (SPSite site = new SPSite(webUrl))
                {
                    setWeb(site.OpenWeb(), true);
                }
            }
    
            #endregion
    
            #region IDisposable Members
    
            ///
            ///Closes inner web object if appropriate.
            ///
            ///2
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            public void Dispose(bool isDisposing)
            {
                if (IsDisposed) return;
                if (isDisposing)
                {
                    doDisposeOfWeb();
                    IsDisposed = true;
                }
            }
    
            #endregion
    
            /// 
            /// Value is true if  method has been called. Object is not in a usable state.
            /// 
            internal bool IsDisposed
            {
                get; private set;
            }
    
            #region IEquatable Members
    
            /// 
            /// This tests whether the two objects wraps the same web. It may however be two different instances of the same web.
            /// 
            /// Another wrapper object.
            /// True if  equals, false otherwise.
            public bool Equals(WebWrapper other)
            {
                if (other == null)
                {
                    return false;
                }
                return Uri.Equals(other.Uri);
            }
    
            #endregion
    
            /// 
            /// Reopens the inner  object. May be used when web object needs to be rereferenced in a new security context.
            /// 
            public void ReOpen()
            {
                bool unsafeSetting = AllowUnsafeUpdatesSetting;
                using (SPSite site = new SPSite(Web.Url))
                {
                    SPWeb newWeb = site.OpenWeb();
                    doDisposeOfWeb();
                    web = newWeb;
                    web.AllowUnsafeUpdates = unsafeSetting;
                    unsafeUpdatesSetting = false;
                    webShouldBeClosed = true;
                }
            }
    
            private void doDisposeOfWeb()
            {
                if (Web == null) return;
                Update(true);
                if (webShouldBeClosed)
                {
                    Web.Close();
                }
                else if (Web.Exists)
                {
                    Web.AllowUnsafeUpdates = unsafeUpdatesSetting;
                }
                web = null;
            }
    
            /// 
            /// Calls  on the  object.
            /// 
            public void Update()
            {
                Update(false);
            }
    
            /// 
            /// Sets  to true.
            /// 
            public void SetUpdatePending()
            {
                UpdatePending = true;
            }
    
            /// 
            /// Calls  on the  object.
            /// If true, update will depend on state of the  property.
            /// 
            public void Update(bool onlyIfPending)
            {
                if (onlyIfPending)
                {
                    if (updatePropertiesPending)
                    {
                        Web.Properties.Update();
                        updatePropertiesPending = false;
                    }
                    if (UpdatePending)
                    {
                        Web.Update();
                        UpdatePending = false;
                    }
                }
                else
                {
                    Web.Update();
                    UpdatePending = false;
                }
            }
    
            /// 
            /// Returns the list from  with  equal to .
            /// 
            /// The  of an existing list.
            /// The first list found with the given title, or null, if no list is found.
            public SPList GetList(string title)
            {
                foreach (SPList list in Web.Lists)
                {
                    if (list.Title == title)
                    {
                        return list;
                    }
                }
                return null;
            }
            /// 
            /// A wrapper method to the  object's  indexer. 
            /// 
            /// The id of the list to return.
            /// The list with the supplied id.
            public SPList GetList(Guid id)
            {
                return Web.Lists[id];
            }
    
            private void setWeb(SPWeb innerWeb, bool shouldBeClosed)
            {
                if (innerWeb == null || !innerWeb.Exists)
                {
                    throw new ArgumentException("Web does not exist", "innerWeb");
                }
                web = innerWeb;
                webShouldBeClosed = shouldBeClosed;
                unsafeUpdatesSetting = innerWeb.AllowUnsafeUpdates;
                AllowUnsafeUpdates();
                webUrl = web.Url;
            }
    
            /// 
            /// Creates a new  object using the
            /// url of the  parameter and wraps it
            /// in a new wrapper object. The web will be
            /// closed when the wrapper is disposed.
            /// The cloning is done using the , thus no security context is transferred to the new web.
            /// 
            /// Use this to create a clone of the context web.
            /// The web to clone.
            /// A new wrapper object.
            public static WebWrapper CloneOf(SPWeb web)
            {
                using (SPSite site = new SPSite(web.Url))
                {
                    return new WebWrapper(site.OpenWeb());
                }
            }
    
    
            /// 
            /// Creates a new  object using the
            ///  of the  parameter and wraps it
            /// in a new wrapper object. The web will be
            /// closed when the wrapper is disposed.
            /// 
            /// Use this to create a clone of the context web.
            /// The wrapper to clone.
            /// A new wrapper object.
            public static WebWrapper CloneOf(WebWrapper web)
            {
                return CloneOf(web.Web);
            }
    
            /// 
            /// Sets the AllowUnsafeUpdates property to true on the
            /// wrapped web object.
            /// 
            /// The setting is resat back in the dispose method, unless the
            /// web itself is closed.
            /// 
            /// 
            public void AllowUnsafeUpdates()
            {
                Web.AllowUnsafeUpdates = true;
            }
    
            /// 
            /// Returns the url of the inner web.
            /// 
            /// A value that equals   property.
            public override string ToString()
            {
                return webUrl;
            }
    
            /// 
            /// Returns a new  object wrapping a new copy of the inner  object.
            /// The cloning is done using the , thus no security context is transferred to the new web.
            /// 
            /// The static method  is used on the  property.
            /// A new wrapper.
            public WebWrapper Clone()
            {
                return CloneOf(Web);
            }
    
            /// 
            /// Implicitly wraps the web object in a  object.
            /// 
            /// The web to wrap.
            /// A new wrapper object. The original web may be accessed through the  property.
            public static implicit operator WebWrapper(SPWeb web)
            {
                return new WebWrapper(web, false);
            }
    
            /// 
            /// Explicitly extracts the  value from the .
            /// 
            /// The object wrapping the  to extract.
            /// The inner  of .
            /// The returned  object should be properly disposed after use.
            public static explicit operator SPWeb(WebWrapper wrapper)
            {
                wrapper.DoNotDisposeInnerWeb();
                return wrapper.Web;
            }
    
            /// 
            /// Wrapper method for  on  object.
            /// 
            /// A site relative uri to the list.
            /// A list if found.
            public SPList GetList(Uri uri)
            {
                return web.GetList(uri.ToString());
            }
    
            /// 
            /// Wrapper method for  on  object.
            /// 
            /// The results of the query,
            public DataTable GetSiteData(SPSiteDataQuery query)
            {
                return Web.GetSiteData(query);
            }
    
            /// 
            /// Creates a new  as a sub web to this.
            /// 
            /// The proposed local url of the new web. The nearest available is selected.
            /// The title of the new web.
            /// The description of the new web.
            /// The language of the new web. If the language is not supported, the language of this is used.
            /// The site template to use.
            /// The new web wrapped in a new  object.
            [DebuggerStepThrough]
            //debugger step through is to prevent this method to break when debugging, as it throws exceptions by [poor] design.
            public WebWrapper CreateSubWeb(string url, string name, string description, uint language,
                                           string template)
            {
                SPWeb newWeb;
                try
                {
                    newWeb = Web.Webs.Add(findSuitableWebUrl(url), name, description, language, template, true, false);
                }
                catch (SPException err)
                {
                    if (err.ErrorCode == -2130575266)
                    {
                        //language not supported. Fallback to parent web language
                        newWeb = Web.Webs.Add(findSuitableWebUrl(url), name, description, Web.Language, template, true,
                                              false);
                    }
                    else
                        throw;
                }
                return new WebWrapper(newWeb);
            }
    
            private string findSuitableWebUrl(string proposedName)
            {
                StringCollection names = new StringCollection();
                names.AddRange(Web.Webs.Names);
                int suffixIndex = 0;
                const int maxIterations = 100000;
                string name = proposedName;
                while (names.Contains(name) && suffixIndex < maxIterations)
                {
                    name = string.Format("{0}_{1}", proposedName, suffixIndex++);
                }
                return name;
            }
    
            /// 
            /// Calling this method will inhibit the default behaviour of closing the web on disposal.
            /// 
            /// Use with caution.
            internal void DoNotDisposeInnerWeb()
            {
                webShouldBeClosed = false;
            }
    
        }
    }
    

提交回复
热议问题