views:

1036

answers:

4

I want to achieve something like the following:

UrlBuilder ub = new UrlBuilder("http://www.google.com/search"); ub.Parameters.Add("q","request"); ub.Parameters.Add("sourceid","ie8");

string uri = ub.ToString(); //http://www.google.com/search?q=request&sourceid=ie8

Is there anything in .NET, or I will have to create my own?

A: 

Does the UriBuilder class help?

It doesn't have any method to add querystring parameters. Look at Query property to set values.

EDIT: See UriTemplate class.

shahkalpesh
shahkalpesh
+2  A: 

Nothing exists that I know of. Here's something simple which does what you want. Usage would be:

        UrlBuilder ub = new UrlBuilder("www.google.com/search")
        .AddQuery("q", "request")
        .AddQuery("sourceid", "ie8");

        string url=ub.ToString();

==

Code is:

    public class UrlBuilder
    {
        private string _authority;
        private string _host;
        private int? _port;
        private Dictionary<string, object> _query = new Dictionary<string, object>();

        public UrlBuilder(string host)
            : this("http", host, null)
        {

        }
        public UrlBuilder(string authority, string host)
            : this(authority, host, null)
        {
        }
        public UrlBuilder(string authority, string host, int? port)
        {
            this._authority = authority;
            this._host = host;
            this._port = port;
        }

        public UrlBuilder AddQuery(string key, object value)
        {
            this._query.Add(key, value);
            return this;
        }

        public override string ToString()
        {
            string url = _authority + "://" + _host;
            if (_port.HasValue)
            {
                url += ":" + _port.ToString();
            }


            return AppendQuery(url);
        }

        private string AppendQuery(string url)
        {
            if (_query.Count == 0)
            {
                return url;
            }

            url += "?";
            bool isNotFirst = false;
            foreach (var key in this._query.Keys)
            {
                if (isNotFirst)
                {
                    url += "&";
                }
                url += HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(this._query[key].ToString());
                isNotFirst = true;
            }

            return url;
        }
    }
}
JoshBerke
@Josh: +1 for the effort. You could also make the AddQuery a method of Uri or UriBuilder class (existing in framework) - just a suggestion.
shahkalpesh
Yep that would work as well, I was actually thinking this would wrap the UriBuilder, but for no particular reason I went this approach
JoshBerke
Josh, I really appreciate your effort.I will surely use it once.This particular time I find the code I posted bellow more convenience with my needs.Thanks, many!
Shimmy
@Shimmy: No problem I was bored and felt like coding something simple, good luck
JoshBerke
thank you so much. i got an inspiration from your boring lol
Shimmy
Awsome good to hear
JoshBerke
Nice, but you can't have duplicate keys. This is rare situation, but it is allowed. Thanks for your code anyway :)
prostynick
Not to hard to extend this to support multiple keys, we'll that as an exercise to the reader:-) But good point none-the less.
JoshBerke
A: 

I developed my own, that's more suitable for my needs, thanks for your code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections.Specialized;

namespace Utils
{
    public class ParameterCollectionBuilder : NameValueCollection
    {
        #region NameValueCollection Implementation
        public ParameterCollectionBuilder() : base() { }
        public ParameterCollectionBuilder(string uri)
        {
            Init(uri);
        }
        public ParameterCollectionBuilder(Uri uri) : this(uri.OriginalString) { }
        public ParameterCollectionBuilder(NameValueCollection baseCollection)
        {
            foreach (string key in baseCollection.Keys) this[key] = baseCollection[key];
            Init(ToString());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseCollection"></param>
        /// <param name="uri"></param>
        /// <remarks>Note: any existing params in the uri will override the params in the collection.</remarks>
        public ParameterCollectionBuilder(NameValueCollection baseCollection, string uri)
        {
            foreach (string key in baseCollection.Keys) this[key] = baseCollection[key];
            Init(uri);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseCollection"></param>
        /// <param name="uri"></param>
        /// <remarks>Note: any existing params in the uri will override the params in the collection.</remarks>
        public ParameterCollectionBuilder(NameValueCollection baseCollection, Uri uri) : this(baseCollection, uri.OriginalString) { }

        public override string ToString()
        {
            return Prefix + Query + Suffix;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <remarks>Overides existing values.</remarks>
        public new void Add(string name, object value)
        {
            Set(name, GetString(value));
        }

        /// <summary>
        /// Add an array of key-value pairs separated by colon char ':'.
        /// </summary>
        /// <param name="names">Invalid items will be ignored.</param>
        public void AddRange(string[] names)
        {
            rangeFlag = true;
            for (int i = 0; i < names.Length; i++)
            {
                string item = names[i];
                item = item.Replace("?", "");
                item = item.Replace("&", "");
                item = item.Replace("=", ":");
                string[] pair = item.Split(':');
                if (pair.Length == 2) Set(pair[0], pair[1]);                
            }
            InitUri(FullString);
            rangeFlag = false;
        }

        public void AppendQueryString(string query)
        {
            Add(BuildCollection(query));
        }

        public void RemoveRange(string[] keys)
        {
            rangeFlag = true;
            foreach (string key in keys)
            {
                Remove(key);  
            } 
            InitUri(FullString);
            rangeFlag = false;
        }


        bool rangeFlag = false;
        public new void Set(string name, object value)
        {
            base.Set(name, GetString(value));
            if (!rangeFlag && Uri != null) InitUri(FullString);
        }

        public override void Remove(string name)
        {
            base.Remove(name);
            if (!rangeFlag && Uri != null) InitUri(FullString);
        }

        public override void Clear()
        {
            base.Clear();
            if (Uri != null) InitUri(FullString);
        }

        #endregion NameValueCollection Implementation

        static string ParseQuery(string uri)
        {
            string query = "";

            if (!uri.Contains('=')) return query;

            int
                start = 0,
                end = uri.Length;

            if (uri.Contains('?')) start = uri.IndexOf('?');
            if (uri.Contains(':')) end = uri.LastIndexOf(':');

            query = uri.Substring(start, (start < end ? end : uri.Length) - start);
            return query;
        }

        void Init(string uri)
        {
            if (Uri == null)
            {
                InitUri(uri);
            }
            OriginalQuery = ParseQuery(uri);
            int qIndex = string.IsNullOrEmpty(OriginalQuery) ? uri.Length : uri.IndexOf(OriginalQuery);
            Prefix = uri.Substring(0, qIndex);
            Suffix = uri.Substring(qIndex + OriginalQuery.Length);
            Merge(OriginalQuery);
        }

        void Merge(string query)
        {
            NameValueCollection col = BuildCollection(query);
            foreach (string key in col.Keys)
            {
                string value = col[key];
                if (!string.IsNullOrEmpty(value)) this[key] = value;
            }
        }
        void InitUri(string uri)
        {
            try
            {
                Uri = new Uri(uri);
            }
            catch { }
        }

        static string GetString(object value)
        {
            return value is string ? value as string : value.ToString();
        }

        static NameValueCollection BuildCollection(string query)
        {
            NameValueCollection collection = new NameValueCollection();
            if (string.IsNullOrEmpty(query) || !query.Contains('=')) return new NameValueCollection();

            //Prepare string
            query = query.ToLower();
            if (!query.StartsWith("?"))
            {
                if (query.Contains('?')) query = query.Substring(query.IndexOf('?'));
            }
            query = query.Replace("?", "");

            foreach (string pair in query.Split('&'))
            {
                string[] separated = pair.Split('=');
                if (separated.Length == 2) collection[separated[0]] = separated[1];
            }

            return collection;
        }

        static string BuildQuery(NameValueCollection parameters)
        {
            string query = "";
            Char separator = '?';
            bool first = true;
            foreach (string key in parameters.Keys)
            {
                query += string.Format("{0}{1}={2}", separator, key, parameters[key]);
                if (first)
                {
                    first = false;
                    separator = '&';
                }
            }
            return query;
        }

        #region Properties
        public Uri Uri { get; private set; }

        public string Prefix { get; private set; }
        public string OriginalQuery { get; private set; }
        public string Suffix { get; private set; }
        public string OriginalString
        {
            get
            {
                return Prefix + OriginalQuery + Suffix;
            }
        }
        public string Query
        {
            get
            {
                return BuildQuery(this);
            }
        }
        public string FullString
        {
            get
            {
                return ToString();
            }
        }
        #endregion Properties
    }
}
Shimmy
+1  A: 

I would recommend you take a look at this article on CodeProject.

The author has extended the System.UriBuilder class and added a QueryString property that behaves in much the same way as the HttpRequest.QueryString property.

Using this class your example would become:

UrlBuilder ub = new UrlBuilder("http://www.google.com/search");
ub.QueryString.Add("q", "request");
ub.QueryString.Add("sourceid", "ie8");
string uri = ub.ToString(); //http://www.google.com/search?q=request&amp;sourceid=ie8

It doesn't have a fluent interface like Josh's solution but could be easily extended to include one.

Nathan Baulch