UserControl rendering: write link to current page? - asp.net

I'm implementing a custom control and in this control I need to write a bunch of links to the current page, each one with a different query parameter. I need to keep existing query string intact, and add (or modify the value of ) an extra query item (eg. "page"):
"Default.aspx?page=1"
"Default.aspx?page=2"
"Default.aspx?someother=true&page=2"
etc.
Is there a simple helper method that I can use in the Render method ... uhmm ... like:
Page.ClientScript.SomeURLBuilderMethodHere(this,"page","1");
Page.ClientScript.SomeURLBuilderMethodHere(this,"page","2");
That will take care of generating a correct URL, maintain existing query string items and not create duplicates eg. page=1&page=2&page=3?
Rolling up my own seems like such an unappealing task.

I'm afraid I don't know of any built-in method for this, we use this method that takes the querystring and sets parameters
/// <summary>
/// Set a parameter value in a query string. If the parameter is not found in the passed in query string,
/// it is added to the end of the query string
/// </summary>
/// <param name="queryString">The query string that is to be manipulated</param>
/// <param name="paramName">The name of the parameter</param>
/// <param name="paramValue">The value that the parameter is to be set to</param>
/// <returns>The query string with the parameter set to the new value.</returns>
public static string SetParameter(string queryString, string paramName, object paramValue)
{
//create the regex
//match paramname=*
//string regex = String.Format(#"{0}=[^&]*", paramName);
string regex = #"([&?]{0,1})" + String.Format(#"({0}=[^&]*)", paramName);
RegexOptions options = RegexOptions.RightToLeft;
// Querystring has parameters...
if (Regex.IsMatch(queryString, regex, options))
{
queryString = Regex.Replace(queryString, regex, String.Format("$1{0}={1}", paramName, paramValue));
}
else
{
// If no querystring just return the Parameter Key/Value
if (queryString == String.Empty)
{
return String.Format("{0}={1}", paramName, paramValue);
}
else
{
// Append the new parameter key/value to the end of querystring
queryString = String.Format("{0}&{1}={2}", queryString, paramName, paramValue);
}
}
return queryString;
}
Obviously you could use the QueryString NameValueCollection property of the URI object to make looking up the values easier, but we wanted to be able to parse any querystring.

Oh and we have this method too that allows you to put in a whole URL string without having to get the querystring out of it
public static string SetParameterInUrl(string url, string paramName, object paramValue)
{
int queryStringIndex = url.IndexOf("?");
string path;
string queryString;
if (queryStringIndex >= 0 && !url.EndsWith("?"))
{
path = url.Substring(0, queryStringIndex);
queryString = url.Substring(queryStringIndex + 1);
}
else
{
path = url;
queryString = string.Empty;
}
return path + "?" + SetParameter(queryString, paramName, paramValue);
}

Related

Get Guid with query string

I get a Guid value in a variable like that
var getvalueGuid = db.Clients.Where(u => u.Numero_telephone ==
TextBox_numero_telephone.Text).Select(u => u.GuID).FirstOrDefault();
And i would like to convert it in a query string like that:
getvalueGuid = Request.QueryString["id"];
How to do?
You can use Guid.TryParse:
Guid getvalueGuid;
if(Guid.TryParse(Request.QueryString["id"], out getvalueGuid))
{
// successfully parsed
}
You will be able to get that inside a QueryString, only if you're having the url like
www.example.com/page?id=[guid_here]
Then when you'll use the code, it would provide you with a String which would contain the Query String provided in the URL.
It's hard to understand your question as you're missing a lot of detail, but I think you want to get a strongly-typed GUID value from the querystring?
System.Guid doesn't have a TryParse method, so you'll have to use the constructor and catch any exceptions thrown:
If so, then do this:
String guidStr = Request.QueryString["id"];
Guid guid = null;
try {
guid = new Guid( guidStr );
} catch(ArgumentNullException) {
} catch(FormatException) {
} catch(OverflowException) {
}
if( guid == null {
// Inform user that the GUID specified was not valid.
}
The three exceptions (ArgumentNullException, FormatException, and OverflowException are documented in the notes for the Guid(String) constructor here: http://msdn.microsoft.com/en-us/library/96ff78dc%28v=vs.110%29.aspx
Update:
I forgot that .NET 4.0 introduced the TryParse method. Use that instead if you're using .NET 4.0 or later: http://msdn.microsoft.com/en-us/library/system.guid.tryparse%28v=vs.110%29.aspx
Guid requestGuid;
if (Guid.TryParse(Request.QueryString["id"], out requestGuid))
{
// Do logic here with requestGuid
}

RouteValueDictionary ignores empty values

I have few empty route values I want in the query string:
var routeValues = new RouteValueDictionary();
routeValues.Add("one", "");
routeValues.Add("two", null);
routeValues.Add("three", string.Empty);
If I then pass it to UrlHelper.RouteUrl() it ignores all the values and the generated query string is empty. However, urls like /?one=&two=&three= are perfectly valid. How can I do that?
This behavior is built into the default Route class. It calls into the ParsedRoute.Bind() method where it does this check:
if (IsRoutePartNonEmpty(obj2))
{
acceptedValues.Add(key, obj2);
}
Which does this:
private static bool IsRoutePartNonEmpty(object routePart)
{
string str = routePart as string;
if (str != null)
{
return (str.Length > 0);
}
return (routePart != null);
}
This effectively prevents any query string values from being output if they are empty. Everything it does is private, static, internal, and otherwise impossible to override. So, there are really only 2 options to override this behavior.
Subclass Route and override GetVirtualPath(), replacing ParsedRoute.Bind() with a custom implementation.
Subclass RouteBase and create a custom route implementation.
I guess the 3rd option is to leave it alone, as others have pointed out this isn't really a problem since having an empty parameter in the query string has little or no value.

How can I resolve ASP.NET "~" app paths to the website root without a Control being present?

I want to Resolve "~/whatever" from inside non-Page contexts such as Global.asax (HttpApplication), HttpModule, HttpHandler, etc. but can only find such Resolution methods specific to Controls (and Page).
I think the app should have enough knowledge to be able to map this outside the Page context. No? Or at least it makes sense to me it should be resolvable in other circumstances, wherever the app root is known.
Update: The reason being I'm sticking "~" paths in the web.configuration files, and want to resolve them from the aforementioned non-Control scenarios.
Update 2: I'm trying to resolve them to the website root such as Control.Resolve(..) URL behaviour, not to a file system path.
Here's the answer:
ASP.Net: Using System.Web.UI.Control.ResolveUrl() in a shared/static function
string absoluteUrl = VirtualPathUtility.ToAbsolute("~/SomePage.aspx");
You can do it by accessing the HttpContext.Current object directly:
var resolved = HttpContext.Current.Server.MapPath("~/whatever")
One point to note is that, HttpContext.Current is only going to be non-null in the context of an actual request. It's not available in the Application_Stop event, for example.
In Global.asax add the following:
private static string ServerPath { get; set; }
protected void Application_BeginRequest(Object sender, EventArgs e)
{
ServerPath = BaseSiteUrl;
}
protected static string BaseSiteUrl
{
get
{
var context = HttpContext.Current;
if (context.Request.ApplicationPath != null)
{
var baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.ApplicationPath.TrimEnd('/') + '/';
return baseUrl;
}
return string.Empty;
}
}
I haven't debugged this sucker but am throwing it our there as a manual solution for lack of finding a Resolve method in the .NET Framework outside of Control.
This did work on a "~/whatever" for me.
/// <summary>
/// Try to resolve a web path to the current website, including the special "~/" app path.
/// This method be used outside the context of a Control (aka Page).
/// </summary>
/// <param name="strWebpath">The path to try to resolve.</param>
/// <param name="strResultUrl">The stringified resolved url (upon success).</param>
/// <returns>true if resolution was successful in which case the out param contains a valid url, otherwise false</returns>
/// <remarks>
/// If a valid URL is given the same will be returned as a successful resolution.
/// </remarks>
///
static public bool TryResolveUrl(string strWebpath, out string strResultUrl) {
Uri uriMade = null;
Uri baseRequestUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority));
// Resolve "~" to app root;
// and create http://currentRequest.com/webroot/formerlyTildeStuff
if (strWebpath.StartsWith("~")) {
string strWebrootRelativePath = string.Format("{0}{1}",
HttpContext.Current.Request.ApplicationPath,
strWebpath.Substring(1));
if (Uri.TryCreate(baseRequestUri, strWebrootRelativePath, out uriMade)) {
strResultUrl = uriMade.ToString();
return true;
}
}
// or, maybe turn given "/stuff" into http://currentRequest.com/stuff
if (Uri.TryCreate(baseRequestUri, strWebpath, out uriMade)) {
strResultUrl = uriMade.ToString();
return true;
}
// or, maybe leave given valid "http://something.com/whatever" as itself
if (Uri.TryCreate(strWebpath, UriKind.RelativeOrAbsolute, out uriMade)) {
strResultUrl = uriMade.ToString();
return true;
}
// otherwise, fail elegantly by returning given path unaltered.
strResultUrl = strWebpath;
return false;
}
public static string ResolveUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("url", "url can not be null or empty");
}
if (url[0] != '~')
{
return url;
}
string applicationPath = HttpContext.Current.Request.ApplicationPath;
if (url.Length == 1)
{
return applicationPath;
}
int startIndex = 1;
string str2 = (applicationPath.Length > 1) ? "/" : string.Empty;
if ((url[1] == '/') || (url[1] == '\\'))
{
startIndex = 2;
}
return (applicationPath + str2 + url.Substring(startIndex));
}
Instead of using MapPath, try using System.AppDomain.BaseDirectory. For a website, this should be the root of your website. Then do a System.IO.Path.Combine with whatever you were going to pass to MapPath without the "~".

Reverse function of HttpUtility.ParseQueryString

.Net's System.Web.HttpUtility class defines the following function to parse a query string into a NameValueCollection:
public static NameValueCollection ParseQueryString(string query);
Is there any function to do the reverse (i.e. to convert a NameValueCollection into a query string)?
System.Collections.Specialized.NameValueCollection does NOT support this, but a derived internal class System.Web.HttpValueCollection DOES (by overriding ToString()).
Unfortunately (being internal) you cannot instantiate this class directly, but one is returned by HttpUtility.ParseQueryString() (and you can call this with String.Empty, but not Null).
Once you have a HttpValueCollection, you can fill it from your original NameValueCollection by calling Add(), before finally calling ToString().
var nameValueCollection = new NameValueCollection {{"a","b"},{"c","d"}};
var httpValueCollection = System.Web.HttpUtility.ParseQueryString(String.Empty);
httpValueCollection.Add(nameValueCollection);
var qs = httpValueCollection.ToString();
nameValueCollection.ToString() = "System.Collections.Specialized.NameValueCollection"
httpValueCollection.ToString() = "a=b&c=d"
A NameValueCollection has an automatic ToString() method that will write all your elements out as a querystring automatically.
you don't need to write your own.
var querystringCollection = HttpUtility.ParseQueryString("test=value1&test=value2");
var output = querystringCollection.ToString();
output = "test=value1&test=value2"
I found that a combination of UriBuilder and HttpUtility classes meets my requirements to manipulate query parameters. The Uri class on its own is not enough, particularly as its Query property is read only.
var uriBuilder = new UriBuilder("http://example.com/something?param1=whatever");
var queryParameters = HttpUtility.ParseQueryString(uriBuilder.Query);
queryParameters.Add("param2", "whatever2");
queryParameters.Add("param3", "whatever2");
uriBuilder.Query = queryParameters.ToString();
var urlString = uriBuilder.Uri.ToString();
The above code results in the URL string: http://example.com/something?param1=whatever&param2=whatever2&param3=whatever2
Note that the ToString() goes via a Uri property, otherwise the output string would have an explicit port 80 in it.
It's nice to be able to do all this using framework classes and not have to write our own code.
I don't think there is a built in one, but here is an example of how to implement http://blog.leekelleher.com/2008/06/06/how-to-convert-namevaluecollection-to-a-query-string/
Here are 2 very useful functions that I use all the time:
private string GetQueryStringParameterValue(Uri url, string key)
{
return HttpUtility.ParseQueryString(url.Query.TrimStart('?'))[key];
}
private Uri SetQueryStringParameterValue(Uri url, string key, string value)
{
var parameters = HttpUtility.ParseQueryString(url.Query.TrimStart('?'));
parameters[key] = value;
var uriBuilder = new UriBuilder(url) { Query = parameters.ToString() };
return uriBuilder.Uri;
}

Return XML from a controller's action in as an ActionResult?

What is the best way to return XML from a controller's action in ASP.NET MVC? There is a nice way to return JSON, but not for XML. Do I really need to route the XML through a View, or should I do the not-best-practice way of Response.Write-ing it?
return this.Content(xmlString, "text/xml");
Use MVCContrib's XmlResult Action.
For reference here is their code:
public class XmlResult : ActionResult
{
private object objectToSerialize;
/// <summary>
/// Initializes a new instance of the <see cref="XmlResult"/> class.
/// </summary>
/// <param name="objectToSerialize">The object to serialize to XML.</param>
public XmlResult(object objectToSerialize)
{
this.objectToSerialize = objectToSerialize;
}
/// <summary>
/// Gets the object to be serialized to XML.
/// </summary>
public object ObjectToSerialize
{
get { return this.objectToSerialize; }
}
/// <summary>
/// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream.
/// </summary>
/// <param name="context">The controller context for the current request.</param>
public override void ExecuteResult(ControllerContext context)
{
if (this.objectToSerialize != null)
{
context.HttpContext.Response.Clear();
var xs = new System.Xml.Serialization.XmlSerializer(this.objectToSerialize.GetType());
context.HttpContext.Response.ContentType = "text/xml";
xs.Serialize(context.HttpContext.Response.Output, this.objectToSerialize);
}
}
}
If you're building the XML using the excellent Linq-to-XML framework, then this approach will be helpful.
I create an XDocument in the action method.
public ActionResult MyXmlAction()
{
// Create your own XDocument according to your requirements
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
return new XmlActionResult(xml);
}
This reusable, custom ActionResult serialises the XML for you.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public Formatting Formatting { get; set; }
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
Formatting = Formatting.None;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting })
_document.WriteTo(writer);
}
}
You can specify a MIME type (such as application/rss+xml) and whether the output should be indented if you need to. Both properties have sensible defaults.
If you need an encoding other than UTF8, then it's simple to add a property for that too.
If you are only interested to return xml through a request, and you have your xml "chunk", you can just do (as an action in your controller):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
There is a XmlResult (and much more) in MVC Contrib. Take a look at http://www.codeplex.com/MVCContrib
I've had to do this recently for a Sitecore project which uses a method to create an XmlDocument from a Sitecore Item and its children and returns it from the controller ActionResult as a File. My solution:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
use one of these methods
public ContentResult GetXml()
{
string xmlString = "your xml data";
return Content(xmlString, "text/xml");
}
or
public string GetXml()
{
string xmlString = "your xml data";
Response.ContentType = "text/xml";
return xmlString;
}
Finally manage to get this work and thought I would document how here in the hopes of saving others the pain.
Environment
VS2012
SQL Server 2008R2
.NET 4.5
ASP.NET MVC4 (Razor)
Windows 7
Supported Web Browsers
FireFox 23
IE 10
Chrome 29
Opera 16
Safari 5.1.7 (last one for Windows?)
My task was on a ui button click, call a method on my Controller (with some params) and then have it return an MS-Excel XML via an xslt transform. The returned MS-Excel XML would then cause the browser to popup the Open/Save dialog. This had to work in all the browsers (listed above).
At first I tried with Ajax and to create a dynamic Anchor with the "download" attribute for the filename,
but that only worked for about 3 of the 5 browsers(FF, Chrome, Opera) and not for IE or Safari.
And there were issues with trying to programmatically fire the Click event of the anchor to cause the actual "download".
What I ended up doing was using an "invisible" IFRAME and it worked for all 5 browsers!
So here is what I came up with:
[please note that I am by no means an html/javascript guru and have only included the relevant code]
HTML (snippet of relevant bits)
<div id="docxOutput">
<iframe id="ifOffice" name="ifOffice" width="0" height="0"
hidden="hidden" seamless='seamless' frameBorder="0" scrolling="no"></iframe></div>
JAVASCRIPT
//url to call in the controller to get MS-Excel xml
var _lnkToControllerExcel = '#Url.Action("ExportToExcel", "Home")';
$("#btExportToExcel").on("click", function (event) {
event.preventDefault();
$("#ProgressDialog").show();//like an ajax loader gif
//grab the basket as xml
var keys = GetMyKeys();//returns delimited list of keys (for selected items from UI)
//potential problem - the querystring might be too long??
//2K in IE8
//4096 characters in ASP.Net
//parameter key names must match signature of Controller method
var qsParams = [
'keys=' + keys,
'locale=' + '#locale'
].join('&');
//The element with id="ifOffice"
var officeFrame = $("#ifOffice")[0];
//construct the url for the iframe
var srcUrl = _lnkToControllerExcel + '?' + qsParams;
try {
if (officeFrame != null) {
//Controller method can take up to 4 seconds to return
officeFrame.setAttribute("src", srcUrl);
}
else {
alert('ExportToExcel - failed to get reference to the office iframe!');
}
} catch (ex) {
var errMsg = "ExportToExcel Button Click Handler Error: ";
HandleException(ex, errMsg);
}
finally {
//Need a small 3 second ( delay for the generated MS-Excel XML to come down from server)
setTimeout(function () {
//after the timeout then hide the loader graphic
$("#ProgressDialog").hide();
}, 3000);
//clean up
officeFrame = null;
srcUrl = null;
qsParams = null;
keys = null;
}
});
C# SERVER-SIDE (code snippet)
#Drew created a custom ActionResult called XmlActionResult which I modified for my purpose.
Return XML from a controller's action in as an ActionResult?
My Controller method (returns ActionResult)
passes the keys parameter to a SQL Server stored proc that generates an XML
that XML is then transformed via xslt into an MS-Excel xml (XmlDocument)
creates instance of the modified XmlActionResult and returns it
XmlActionResult result = new XmlActionResult(excelXML, "application/vnd.ms-excel");
string version = DateTime.Now.ToString("dd_MMM_yyyy_hhmmsstt");
string fileMask = "LabelExport_{0}.xml";
result.DownloadFilename = string.Format(fileMask, version);
return result;
The main modification to the XmlActionResult class that #Drew created.
public override void ExecuteResult(ControllerContext context)
{
string lastModDate = DateTime.Now.ToString("R");
//Content-Disposition: attachment; filename="<file name.xml>"
// must set the Content-Disposition so that the web browser will pop the open/save dialog
string disposition = "attachment; " +
"filename=\"" + this.DownloadFilename + "\"; ";
context.HttpContext.Response.Clear();
context.HttpContext.Response.ClearContent();
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.Cookies.Clear();
context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);// Stop Caching in IE
context.HttpContext.Response.Cache.SetNoStore();// Stop Caching in Firefox
context.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero);
context.HttpContext.Response.CacheControl = "private";
context.HttpContext.Response.Cache.SetLastModified(DateTime.Now.ToUniversalTime());
context.HttpContext.Response.ContentType = this.MimeType;
context.HttpContext.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
//context.HttpContext.Response.Headers.Add("name", "value");
context.HttpContext.Response.Headers.Add("Last-Modified", lastModDate);
context.HttpContext.Response.Headers.Add("Pragma", "no-cache"); // HTTP 1.0.
context.HttpContext.Response.Headers.Add("Expires", "0"); // Proxies.
context.HttpContext.Response.AppendHeader("Content-Disposition", disposition);
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, this.Encoding)
{ Formatting = this.Formatting })
this.Document.WriteTo(writer);
}
That was basically it.
Hope it helps others.
A simple option that will let you use streams and all that is return File(stream, "text/xml");.
Here is a simple way of doing it:
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
MemoryStream ms = new MemoryStream();
xml.Save(ms);
return File(new MemoryStream(ms.ToArray()), "text/xml", "HelloWorld.xml");
A small variation of the answer from Drew Noakes that use the method Save() of XDocument.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
_document.Save(context.HttpContext.Response.OutputStream)
}
}

Resources