diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs
new file mode 100644
index 0000000000..68c29c27f1
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs
@@ -0,0 +1,162 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using Microsoft.AspNet.Mvc.Rendering;
+using Microsoft.AspNet.Mvc.TagHelpers.Internal;
+using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+
+namespace Microsoft.AspNet.Mvc.TagHelpers
+{
+ ///
+ /// implementation targeting <a> elements.
+ ///
+ [TagName("a")]
+ public class AnchorTagHelper : TagHelper
+ {
+ private const string RouteAttributePrefix = "route-";
+
+ [Activate]
+ private ViewContext ViewContext { get; set; }
+
+ [Activate]
+ private IHtmlGenerator Generator { get; set; }
+
+ ///
+ /// The name of the action method.
+ ///
+ /// Cannot be provided if is specified.
+ public string Action { get; set; }
+
+ ///
+ /// The name of the controller.
+ ///
+ /// Cannot be provided if is specified.
+ public string Controller { get; set; }
+
+ ///
+ /// The protocol for the URL, such as "http" or "https".
+ ///
+ public string Protocol { get; set; }
+
+ ///
+ /// The host name for the URL.
+ ///
+ public string Host { get; set; }
+
+ ///
+ /// The URL fragment name (the anchor name).
+ ///
+ public string Fragment { get; set; }
+
+ ///
+ /// Name of the route.
+ ///
+ /// Cannot be provided if or is specified.
+ public string Route { get; set; }
+
+ ///
+ /// Does nothing if user provides a "href" attribute. Cannot specify a "href" attribute AND
+ /// , or .
+ public override void Process(TagHelperContext context, TagHelperOutput output)
+ {
+ // If there's an "href" on the tag it means it's being used as a normal anchor.
+ if (output.Attributes.ContainsKey("href"))
+ {
+ if (Action != null || Controller != null || Route != null)
+ {
+ // User specified an href AND a Action, Controller or Route; can't determine the href attribute.
+ throw new InvalidOperationException(
+ Resources.FormatAnchorTagHelper_CannotDetermineHrefOneSpecified(
+ nameof(AnchorTagHelper),
+ nameof(Action),
+ nameof(Controller),
+ nameof(Route)));
+ }
+
+ // User is using the AnchorTagHelper as normal anchor tag, restore any provided attributes.
+ RestoreBoundHtmlAttributes(context, output);
+ }
+ else
+ {
+ TagBuilder tagBuilder;
+
+ var prefixedValues = TagHelperOutputHelper.PullPrefixedAttributes(RouteAttributePrefix, output);
+
+ // Generator.GenerateActionLink || GenerateRouteLink does not accept a Dictionary for
+ // route values.
+ var routeValues = prefixedValues.ToDictionary(
+ attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
+ attribute => (object)attribute.Value);
+
+ if (Route == null)
+ {
+ tagBuilder = Generator.GenerateActionLink(linkText: string.Empty,
+ actionName: Action,
+ controllerName: Controller,
+ protocol: Protocol,
+ hostname: Host,
+ fragment: Fragment,
+ routeValues: routeValues,
+ htmlAttributes: null);
+ }
+ else if (Action != null || Controller != null)
+ {
+ // Route and Action or Controller were specified. Can't determine the href attribute.
+ throw new InvalidOperationException(
+ Resources.FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(
+ nameof(AnchorTagHelper),
+ nameof(Route),
+ nameof(Action),
+ nameof(Controller)));
+ }
+ else
+ {
+ tagBuilder = Generator.GenerateRouteLink(linkText: string.Empty,
+ routeName: Route,
+ protocol: Protocol,
+ hostName: Host,
+ fragment: Fragment,
+ routeValues: routeValues,
+ htmlAttributes: null);
+ }
+
+ TagHelperOutputHelper.MergeAttributes(tagBuilder, output);
+ }
+ }
+
+ private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output)
+ {
+ if (Action != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Action), context, output);
+ }
+
+ if (Controller != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Controller), context, output);
+ }
+
+ if (Protocol != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Protocol), context, output);
+ }
+
+ if (Host != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Host), context, output);
+ }
+
+ if (Fragment != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Fragment), context, output);
+ }
+
+ if (Route != null)
+ {
+ TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Route), context, output);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs
index 3497a16522..a8561eb3d7 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs
@@ -10,6 +10,38 @@ internal static class Resources
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Mvc.TagHelpers.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ ///
+ /// Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute.
+ ///
+ internal static string AnchorTagHelper_CannotDetermineHrefOneSpecified
+ {
+ get { return GetString("AnchorTagHelper_CannotDetermineHrefOneSpecified"); }
+ }
+
+ ///
+ /// Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute.
+ ///
+ internal static string FormatAnchorTagHelper_CannotDetermineHrefOneSpecified(object p0, object p1, object p2, object p3)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefOneSpecified"), p0, p1, p2, p3);
+ }
+
+ ///
+ /// Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute.
+ ///
+ internal static string AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified
+ {
+ get { return GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"); }
+ }
+
+ ///
+ /// Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute.
+ ///
+ internal static string FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"), p0, p1, p2, p3);
+ }
+
///
/// Cannot determine an {1} for {0}. A {0} with a speicified URL based {1} must not have a {2} attribute.
///
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx
index a0b6303df9..8307027f3b 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx
@@ -117,6 +117,12 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute.
+
+
+ Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute.
+
Cannot determine an {1} for {0}. A {0} with a speicified URL based {1} must not have a {2} attribute.