1

When rendering an external link from a General Link field using the Glass Mapper BeginRenderLink helper, any query string parameters without values are getting an equals sign (=) added after them.

An example

The URL

http://website.com/page?param1&param2=very_yes

becomes

http://website.com/page?param1=&param2=very_yes

The view markup

<div class="button">
    @using (BeginRenderLink(x => x.ButtonURL, new { @class = "button-highlight"}))
    {
        @Model.ButtonText
    }
</div>

For most systems, the difference between a query string parameter having an empty value and no value does not impact the functionality. In our case, however, we're linking to a system that throws an error if the value exists but is empty. Is there a way to fix this issue?

1 Answer 1

1

This issue is caused by how Glass Mapper generates URLs with its UrlBuilder:

public override string ToString()
{
    StringBuilder sb = new StringBuilder();

    // [get the first part of the URL] (omitted for brevity)

    foreach (var keyValuePair in QueryString)
    {
        if (keyValuePair.Key.HasValue())
        {
            sb.AppendFormat("{0}={1}{2}", keyValuePair.Key ?? string.Empty, keyValuePair.Value ?? string.Empty,QuerySeparator);
        }
    }

    // [remove the last separator] (omitted for brevity)

    return sb.ToString();
}

Note the line where it appends the next query string parameter concatenated with an = and its value, even if the value is empty.

The fix

I didn't want to change this functionality in Glass Mapper, because I figured it was actually MORE likely to be what people want. But for our case I needed a solution, so here's what I came up with:

Create a custom GlassHtml utility

Override the default BeginRenderLink behavior by replacing any offending contents of the generated URL.

public class CustomGlassHtml : GlassHtml
{
    public CustomGlassHtml(ISitecoreContext sitecoreContext) : base(sitecoreContext)
    {
    }

    public override RenderingResult BeginRenderLink<T>(T model, Expression<Func<T, object>> field, TextWriter writer, object parameters = null, bool isEditable = false, bool alwaysRender = false)
    {
        // We use a StringWriter to capture the rendering output from Glass so we can 
        // prevent the case where Glass' UrlBuilder adds a = after a valueless parameter.
        // For example:
        //   http://website.com/page?param1&param2=very_yes
        //   becomes
        //   http://website.com/page?param1=&param2=very_yes

        var glassRenderedLink = new StringBuilder();
        var stringWriter = new StringWriter(glassRenderedLink);

        // Call the standard BeginRenderLink. When the RenderingResult is created, it immediately writes the "first part" to the passed in writer
        RenderingResult renderingResult = base.BeginRenderLink(model, field, stringWriter, parameters, isEditable, alwaysRender);
        string firstPart = glassRenderedLink.ToString();
        glassRenderedLink.Clear();

        // When the RenderingResult is disposed, it writes the "last part" to its writer
        renderingResult.Dispose();
        string lastPart = glassRenderedLink.ToString();

        // Then we fix the URL problem
        firstPart = firstPart.Replace("=&", "&");

        // And return our own RenderingResult
        return new RenderingResult(writer, firstPart, lastPart);
    }
}

Create a custom GlassHtmlFactory to return your CustomGlassHtml

public class CustomGlassHtmlFactory : IGlassHtmlFactory
{
    public IGlassHtml GetGlassHtml(ISitecoreContext sitecoreContext)
    {
        return new CustomGlassHtml(sitecoreContext);
    }
}

Set your CustomGlassHtmlFactory as the GlassHtmlFactory in the GlassMapperScCustom class

public static IDependencyResolver CreateResolver()
{
    var config = new Glass.Mapper.Sc.Config();

    var dependencyResolver = new DependencyResolver(config);
    // add any changes to the standard resolver here

    // Register our custom GlassHtml class
    dependencyResolver.GlassHtmlFactory = new CustomGlassHtmlFactory();

    dependencyResolver.Finalise();

    return dependencyResolver;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.