Navigate to views in prism containing danish letters - unity-container

I have views with ÆØÅ in there names. If I try to navigate to one of them I get system.object instead of my view.
unity.RegisterTypeForNavigation();
public static void RegisterTypeForNavigation<T>(this IUnityContainer container)
{
container.RegisterType(typeof(object), typeof(T), typeof(T).FullName);
}
Should I escape the FullName?

Yes you should url encode the type name, fx. like this
public static class UnityExtensions
{
public static void RegisterTypeForNavigation<T>(this IUnityContainer container)
{
container.RegisterType(typeof(object), typeof(T),Replace(typeof(T).FullName));
}
public static string Replace(string url)
{
return WebUtility.UrlEncode(url);
}
}
And you also have to urlencode when you navigate.
public void Navigate(object navigatePath, string region = RegionNames.ContentRegion,
NavigationParameters parameters = null,
Action<NavigationResult> navigationCallback = null)
{
if (navigatePath == null) return;
var path = UnityExtensions.Replace(navigatePath.ToString());
if (parameters == null)
_regionManager.RequestNavigate(region, path);
else
{
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
if (navigationCallback == null)
_regionManager.RequestNavigate(region, uri, parameters);
else
_regionManager.RequestNavigate(region, uri, navigationCallback, parameters);
}
}
That's it

Related

ASP.NET Core 2.1 How to pass variables to TypeFilter

I have created this typefilter that is supposed to take 2 variables in order for it to send to a method that is linked to the filter. However, I am unable to attach my 2 variables for it to run.
public class RolesFilterAttribute : TypeFilterAttribute
{
public RolesFilterAttribute() : base(typeof(RolesFilterAttributeImpl))
{
}
private class RolesFilterAttributeImpl : IActionFilter
{
private readonly ValidateRoleClient validateRoleClient;
private string Role;
private string SecretKey;
public RolesFilterAttributeImpl(string Role, string SecretKey, ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
this.Role = Role;
this.SecretKey = SecretKey;
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = Role,
SecretKey = SecretKey,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
public void OnActionExecuting(ActionExecutingContext context)
{
throw new NotImplementedException();
}
}
}
This is how I declare the filter and it compiles fine. However, I am not able to pass the required variables which are SecretKey and Role through it. Is my typefilter declared correctly?
[TypeFilter(typeof(RolesFilterAttribute))]
public IActionResult About()
{
return View();
}
Taken from the official documentation
[TypeFilter(typeof(AddHeaderAttribute),
Arguments = new object[] { "Author", "Steve Smith (#ardalis)" })]
public IActionResult Hi(string name)
{
return Content($"Hi {name}");
}

add mvc3 Unobtrusive validation Min/max validators

I'm trying to implement client validation for my custom type, however i'm not sure how to do it for min/max validators.
model:
[MultilanguagePropertyRequired(ErrorMessageResourceName = "fld_Description_val_Required", ErrorMessageResourceType = typeof(Resources.Service.Controllers.Firm))]
[MultilanguagePropertyStringLength(1000, MinimumLength = 150, ErrorMessageResourceName = "fld_Description_val_MinLength_lbl", ErrorMessageResourceType = typeof(Resources.Service.Controllers.Firm))]
[Display(Name = "fld_Description_lbl", ResourceType = typeof(Resources.Service.Controllers.Firm))]
public MultilanguageProperty<string> Description
{
get
{
return this.GetMultilanguageProperty("Description", string.Empty, this);
}
set
{
this.SetMultilanguageProperty("Description", value);
}
}
this is my custom string length attribute that extends "StringLegth":
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class MultilanguagePropertyStringLengthAttribute : StringLengthAttribute, IClientValidatable
{
public MultilanguagePropertyStringLengthAttribute(int length) : base(length)
{
}
public override bool IsValid(object value)
{
string strValue = (string)(value as MultilanguageProperty<string>).Value;
return base.IsValid(strValue);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule() { ValidationType = "multilanguagestringlength", ErrorMessage = this.ErrorMessageString };
}
}
then on my view I have this:
..
<script type="text/javascript">
(function ($) {
$.validator.unobtrusive.adapters.addBool("multilanguagerequired", "required");
$.validator.unobtrusive.adapters.addMinMax("multilanguagestringlength", "minlength", "maxlength");
} (jQuery));
</script>
..
which doesn't work. am i missing something here?
thanks
No need to have a custom client side validation. You can try something like this:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ValidatePasswordLengthAttribute : ValidationAttribute, IClientValidatable {
private const string _defaultErrorMessage = "'{0}' must be between {1} and {2} characters long.";
private readonly int _minCharacters, _maxCharacters;
public ValidatePasswordLengthAttribute(int minLength, int maxLength)
: base(_defaultErrorMessage) {
_minCharacters = minLength;
_maxCharacters = maxLength;
}
public override string FormatErrorMessage(string name) {
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
name, _minCharacters, _maxCharacters);
}
public override bool IsValid(object value) {
string valueAsString = value as string;
return (valueAsString != null && valueAsString.Length >= _minCharacters && valueAsString.Length <= _maxCharacters);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
return new[]{
new ModelClientValidationStringLengthRule(FormatErrorMessage(metadata.GetDisplayName()), _minCharacters, _maxCharacters)
};
}
}
It comes from the build-in MVC3 internet project template.
I am solving similar problem in a different way, but hey, try some parts of this code! I mean GetClientValidationRules() method for string length validation.
public sealed class MyStringLengthAttribute : StringLengthAttribute, IClientValidatable
{
private int? labelId;
public MyStringLengthAttribute(int label, int maximumLength)
: base(maximumLength)
{
labelId = label;
}
public override string FormatErrorMessage(string name)
{
if (labelId.HasValue)
{
return String.Format(MyLabel.Label(labelId.Value), name);
}
return String.Format(MyLabel.Default("FieldTooLong_Validation", "Field {0} is too long"), name);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
StringLengthAttributeAdapter adapt = new StringLengthAttributeAdapter(metadata,context, this);
return adapt.GetClientValidationRules();
}
}
I use the Adapter class I found in .NET framework, and no need for the custom Javascript work.
And follow this if you still want to do custom JS part http://itmeze.com/2010/12/06/checkbox-has-to-be-checked-with-unobtrusive-jquery-validation-and-asp-net-mvc-3/
I too am having the same problem. You are missing ValidationParameters in your GetClientValidationRules:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "dayrange"
};
rule.ValidationParameters.Add("min", _minimumDays);
rule.ValidationParameters.Add("max", _maximumDays);
yield return rule;
}
I still having problems with the wiring between the jQuery.validator and jQuery.validator.unobtrusive.adapters but here's the code. Hope it helps:
$(function () {
jQuery.validator.addMethod('dayRange', function (value, element, param) {
if (!value) return false;
var valueDateParts = value.split(param.seperator);
var minDate = new Date();
var maxDate = new Date();
var now = new Date();
var dateValue = new Date(valueDateParts[2],
(valueDateParts[1] - 1),
valueDateParts[0],
now.getHours(),
now.getMinutes(),
(now.getSeconds()+5));
minDate.setDate(minDate.getDate() - parseInt(param.min));
maxDate.setDate(maxDate.getDate() + parseInt(param.max));
return dateValue >= minDate && dateValue <= maxDate;
});
jQuery.validator.unobtrusive.adapters.addMinMax('dayrange', 'minlength', 'maxlength', 'dayrange');
}(jQuery));

How to generate documentation for Asp.Net MVC?

With .net 4.0/Preview kit 2, we can generate help pages for WCF REST.
Is there anyway we can do the same for MVC ?
www.somewebsite.com/search/help
I can create help pages (views) and expose them.
I can generate XSD schema and spit out as xml.
Any other guidance/suggestions ?
I want to generate something similar to this.
UriTemplate http://somewebsite.com/Search/
Method PUT
Response Format Xml
Response Schema http://somewebsite.com/help/response/schema
Response Example http://somewebsite.com/help/response/example
Update:
I was able to create documentation by using below code.
Route : somewebsite.com/Media/
HelpRoute : somewebsite.com/Media/Help (Append help to the parent route)
Add routes accordingly. See below example.
routes.MapRoute("Search",
"Search/Quick/",
new { controller = "Search", action = "Search" },
new { httpMethod = new HttpMethodConstraint("PUT") }
);
routes.MapRoute("SearchHelp",
"Search/Quick/Help",
new { controller = "Search", action = "Search" },
new { helpConstraint = new HelpConstraint { Help = true, SampleType = typeof(Search) } }
);
public class HelpResult : ViewResult
{
private string HelpPage { get; set; }
private string folderName = "HelpFiles";
private HttpServerUtilityBase server { get; set; }
private Type sampleType { get; set; }
private string schemaName { get; set; }
const string htmlFormat =
#"
<html>
<head>
<title>Help</title>
</head>
<body>
<li>URL - {0}</li>
<li>Verb - {1}</li>
{2}
</body>
</html>
";
public override void ExecuteResult(ControllerContext context)
{
server = context.HttpContext.Server;
StringBuilder parentUrl = new StringBuilder();
var data = context.RouteData.Route.GetRouteData(context.HttpContext);
//Getting requested route url.
string url = ((Route)(data.Route)).Url;
//Getting parent route from requested route url by removing "Help" from the route.
string newUrl = url.Substring(0, url.Length - 4);
parentUrl.Append("/" + newUrl);
sampleType = data.GetSampleType();
var validVerbs = GetValidVerbs(MakeAppRelative(parentUrl.ToString()));
CreateSchema(sampleType, true);
HelpPage = string.Format(htmlFormat, newUrl, validVerbs, CreateInputSampleText());
context.HttpContext.Response.Output.Write(HelpPage);
}
private string CreateInputSampleText()
{
if (sampleType != null && !string.IsNullOrEmpty(sampleType.Name))
{
string sampleText =
#"<li>Input Sample Xml - <a href='\HelpFiles\{0}.xml'>Click Here</a></li>
<li>Input Sample Json - <a href='\HelpFiles\{0}.txt'>Click Here</a></li>
<li>Input Schema - <a href='\HelpFiles\{1}'>Click Here</a></li>";
sampleText = string.Format(sampleText, sampleType.Name, schemaName);
return sampleText;
}
return string.Empty;
}
private static string MakeAppRelative(string url)
{
if (!url.StartsWith("~"))
{
if (!url.StartsWith("/"))
url = "~/" + url;
else
url = "~" + url;
}
return url;
}
private static string GetValidVerbs(string Url)
{
StringBuilder validVerbs = new StringBuilder();
var httpMethodOptions = new[] { "GET", "POST", "PUT", "DELETE", "HEAD" };
foreach (var httpMethodOption in httpMethodOptions)
{
var fakeContext = new FakeHttpContext(MakeAppRelative(Url), httpMethodOption);
foreach (Route route in RouteTable.Routes)
{
var rd = route.GetRouteData(fakeContext);
if (rd != null)
{
bool errorControllerApplied = route.IsErrorController();
if (!errorControllerApplied)
{
validVerbs.Append(httpMethodOption);
}
}
}
}
return validVerbs.ToString();
}
private void CreateFile(Type type, Stream stream)
{
using (Stream inputStream = stream)
{
schemaName = sampleType + "Schema.xml";
string folder = Path.GetFullPath(Path.Combine(server.MapPath("~"), folderName));
string file = Path.Combine(folder, schemaName);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
using (FileStream fileStream = new FileStream(file, FileMode.Create))
{
byte[] fileContent = new byte[inputStream.Length];
inputStream.Read(fileContent, 0, fileContent.Length);
fileStream.Write(fileContent, 0, fileContent.Length);
fileStream.Flush();
}
}
}
private void CreateSchema(Type type, bool isXmlSerializerType)
{
System.Collections.IEnumerable schemas;
if (isXmlSerializerType)
{
XmlReflectionImporter importer = new XmlReflectionImporter();
XmlTypeMapping typeMapping = importer.ImportTypeMapping(type);
XmlSchemas s = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(s);
exporter.ExportTypeMapping(typeMapping);
schemas = s.GetSchemas(null);
}
else
{
XsdDataContractExporter exporter = new XsdDataContractExporter();
exporter.Export(type);
schemas = exporter.Schemas.Schemas();
}
using (MemoryStream stream = new MemoryStream())
{
XmlWriterSettings xws = new XmlWriterSettings() { Indent = true };
using (XmlWriter w = XmlWriter.Create(stream, xws))
{
w.WriteStartElement("Schemas");
foreach (XmlSchema schema in schemas)
{
if (schema.TargetNamespace != "http://www.w3.org/2001/XMLSchema")
{
schema.Write(w);
}
}
}
stream.Seek(0, SeekOrigin.Begin);
CreateFile(type, stream);
}
}
public static class RouteDataExtensions
{
public static bool IsHelpConstraintApplied(this RouteData data)
{
if (data != null && data.Route != null)
{
var constraints = ((Route) (data.Route)).Constraints;
var helpConstraint = (from c in constraints.Values
where c.GetType().Equals(typeof (HelpConstraint))
select c).FirstOrDefault();
if (helpConstraint != null)
{
return true;
}
}
return false;
}
public static Type GetSampleType(this RouteData data)
{
if (data != null && data.Route != null)
{
var constraints = ((Route)(data.Route)).Constraints;
var helpConstraint = (from c in constraints.Values
where c.GetType().Equals(typeof(HelpConstraint))
select c).FirstOrDefault();
if (helpConstraint != null)
{
return ((HelpConstraint) helpConstraint).SampleType;
}
}
return null;
}
public static string GetMethodType(this RouteData data)
{
if (data != null && data.Route != null)
{
var constraints = ((Route) (data.Route)).Constraints;
var httpMethodConstraint = (from c in constraints.Values
where c.GetType().Equals(typeof (HttpMethodConstraint))
select c).FirstOrDefault();
if (httpMethodConstraint != null)
{
return ((HttpMethodConstraint) httpMethodConstraint).AllowedMethods.Single();
}
}
return null;
}
public static bool IsErrorController(this Route data)
{
if (data != null)
{
var defaults = ((Route)(data)).Defaults;
var controllerName = (from c in defaults.Values
where c.ToString().Contains("Error")
select c).FirstOrDefault();
if (controllerName != null)
{
return true;
}
}
return false;
}
public static RouteData GetRouteDataByUrl(this string url)
{
string httpMethod = "PUT";
var fakeContext = new FakeHttpContext(MakeAppRelative(url), httpMethod);
return RouteTable.Routes.GetRouteData(fakeContext);
}
private static string MakeAppRelative(string url)
{
if (!url.StartsWith("~"))
{
if (!url.StartsWith("/"))
url = "~/" + url;
else
url = "~" + url;
}
return url;
}
}
public class HelpConstraint : IRouteConstraint
{
public bool Help { get; set; }
public Type SampleType { get; set; }
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if(Help)
{
return true;
}
return false;
}
}
References
http://stephenwalther.com/blog/archive/2008/08/03/asp-net-mvc-tip-29-build-a-controller-to-debug-your-custom-routes.aspx
http://aspnet.codeplex.com/releases/view/24644
This code is not bug free. Please post improvements if you have any. Use it at your own risk.
Not exactly sure what you are looking for, but you can check out GhostDoc:
http://submain.com/products/ghostdoc.aspx
I use this to generate XML documentation in MVC.
Most probably you have solved your issue by now. Anyway, I think you need IApiExplorer. Have a look at this blog.

Structuremap error when using HttpContextBase in constructor

I am building a ASP.NET MVC 2.0 app on .NET 4.0 and am using Structuremap 2.6.1 for IoC. I recently added a ICookie and Cookie class, the Cookie class takes HttpContextBase as a constructor parameter (See below) and now when I run my app I get this error :No Default Instance defined for PluginFamily System.Web.HttpContextBase.
I have used this method before in another MVC app with the same stack but did not get this error. Am I missing something? If I do need to add some mapping code for HttoContextBase in my structuremap configuration file what would I use?
And help would be great!!!
Cookie.cs
public class Cookie : ICookie
{
private readonly HttpContextBase _httpContext;
private static bool defaultHttpOnly = true;
private static float defaultExpireDurationInDays = 1;
private readonly ICryptographer _cryptographer;
public Cookie(HttpContextBase httpContext, ICryptographer cryptographer)
{
Check.Argument.IsNotNull(httpContext, "httpContext");
Check.Argument.IsNotNull(cryptographer, "cryptographer");
_cryptographer = cryptographer;
_httpContext = httpContext;
}
public static bool DefaultHttpOnly
{
[DebuggerStepThrough]
get { return defaultHttpOnly; }
[DebuggerStepThrough]
set { defaultHttpOnly = value; }
}
public static float DefaultExpireDurationInDays
{
[DebuggerStepThrough]
get { return defaultExpireDurationInDays; }
[DebuggerStepThrough]
set
{
Check.Argument.IsNotZeroOrNegative(value, "value");
defaultExpireDurationInDays = value;
}
}
public T GetValue<T>(string key)
{
return GetValue<T>(key, false);
}
public T GetValue<T>(string key, bool expireOnceRead)
{
var cookie = _httpContext.Request.Cookies[key];
T value = default(T);
if (cookie != null)
{
if (!string.IsNullOrWhiteSpace(cookie.Value))
{
var converter = TypeDescriptor.GetConverter(typeof(T));
try
{
value = (T)converter.ConvertFromString(_cryptographer.Decrypt(cookie.Value));
}
catch (NotSupportedException)
{
if (converter.CanConvertFrom(typeof(string)))
{
value = (T)converter.ConvertFrom(_cryptographer.Decrypt(cookie.Value));
}
}
}
if (expireOnceRead)
{
cookie = _httpContext.Response.Cookies[key];
if (cookie != null)
{
cookie.Expires = DateTime.Now.AddDays(-100d);
}
}
}
return value;
}
public void SetValue<T>(string key, T value)
{
SetValue(key, value, DefaultExpireDurationInDays, DefaultHttpOnly);
}
public void SetValue<T>(string key, T value, float expireDurationInDays)
{
SetValue(key, value, expireDurationInDays, DefaultHttpOnly);
}
public void SetValue<T>(string key, T value, bool httpOnly)
{
SetValue(key, value, DefaultExpireDurationInDays, httpOnly);
}
public void SetValue<T>(string key, T value, float expireDurationInDays, bool httpOnly)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
string cookieValue = string.Empty;
try
{
cookieValue = converter.ConvertToString(value);
}
catch (NotSupportedException)
{
if (converter.CanConvertTo(typeof(string)))
{
cookieValue = (string)converter.ConvertTo(value, typeof(string));
}
}
if (!string.IsNullOrWhiteSpace(cookieValue))
{
var cookie = new HttpCookie(key, _cryptographer.Encrypt(cookieValue))
{
Expires = DateTime.Now.AddDays(expireDurationInDays),
HttpOnly = httpOnly
};
_httpContext.Response.Cookies.Add(cookie);
}
}
}
IocMapping.cs
public class IoCMapping
{
public static void Configure()
{
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ProjectName.Core.Properties.Settings.ProjectNameConnectionString"].ConnectionString;
MappingSource mappingSource = new AttributeMappingSource();
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.Assembly("ProjectName.Core");
scan.Assembly("ProjectName.WebUI");
scan.WithDefaultConventions();
});
x.For<IUnitOfWork>().HttpContextScoped().Use<UnitOfWork>();
x.For<IDatabase>().HttpContextScoped().Use<Database>().Ctor<string>("connection").Is(connectionString).Ctor<MappingSource>("mappingSource").Is(mappingSource);
x.For<ILogger>().Singleton().Use<NLogLogger>();
x.For<ICacheManager>().Singleton().Use<CacheManager>().Ctor<System.Web.Caching.Cache>().Is(HttpRuntime.Cache);
x.For<IEmailSender>().Singleton().Use<EmailSender>();
x.For<IAuthenticationService>().HttpContextScoped().Use<AuthenticationService>();
x.For<ICryptographer>().Use<Cryptographer>();
x.For<IUserSession>().HttpContextScoped().Use<UserSession>();
x.For<ICookie>().HttpContextScoped().Use<Cookie>();
x.For<ISEORepository>().HttpContextScoped().Use<SEORepository>();
x.For<ISpotlightRepository>().HttpContextScoped().Use<SpotlightRepository>();
x.For<IContentBlockRepository>().HttpContextScoped().Use<ContentBlockRepository>();
x.For<ICatalogRepository>().HttpContextScoped().Use<CatalogRepository>();
x.For<IPressRoomRepository>().HttpContextScoped().Use<PressRoomRepository>();
x.For<IEventRepository>().HttpContextScoped().Use<EventRepository>();
x.For<IProductRegistrationRepository>().HttpContextScoped().Use<ProductRegistrationRepository>();
x.For<IWarrantyRepository>().HttpContextScoped().Use<WarrantyRepository>();
x.For<IInstallerRepository>().HttpContextScoped().Use<InstallerRepository>();
x.For<ISafetyNoticeRepository>().HttpContextScoped().Use<SafetyNoticeRepository>();
x.For<ITradeAlertRepository>().HttpContextScoped().Use<TradeAlertRepository>();
x.For<ITestimonialRepository>().HttpContextScoped().Use<TestimonialRespository>();
x.For<IProjectPricingRequestRepository>().HttpContextScoped().Use<ProjectPricingRequestRepository>();
x.For<IUserRepository>().HttpContextScoped().Use<UserRepository>();
x.For<IRecipeRepository>().HttpContextScoped().Use<RecipeRepository>();
});
LogUtility.Log.Info("Registering types with StructureMap");
}
}
I believe you would need to register the HttpContextBase on every request in your Begin_Request handler like so:
For<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
Update: Make sure you register a lambda, otherwise you StructureMap will store the HttpContext available at registration time as a singleton.

Is there a URL builder that supports request parameter concatenation as well?

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?
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;
}
}
}
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.
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
}
}
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&sourceid=ie8
It doesn't have a fluent interface like Josh's solution but could be easily extended to include one.
With Flurl [disclosure: I'm the author], your example would look like this:
string uri = "http://www.google.com/search"
.SetQueryParams(new { q = "request", sourceid = "ie8" });
The basic URL builder is available via NuGet:
PM> Install-Package Flurl
There's also a new companion lib that extends Flurl with fluent, testable HTTP:
PM> Install-Package Flurl.Http

Resources