Adding URL generation to WebFX for controllers and views

This follows a similar pattern to html helpers - a minimal basis interface
that performs the main functionality, and a set of extension methods that
make up the rich API.
This commit is contained in:
Ryan Nowak 2014-03-05 17:17:12 -08:00
parent 9af7c2bbfb
commit 0ce2c511d4
14 changed files with 144 additions and 7 deletions

View File

@ -0,0 +1,23 @@

using Microsoft.AspNet.Mvc;
namespace MvcSample
{
public class LinkController : Controller
{
public IActionResult Details()
{
return View();
}
public string About()
{
return Url.Action(null);
}
public string Get()
{
return Url.Route(new { controller = "Home", action = "Details" });
}
}
}

View File

@ -49,6 +49,7 @@ namespace MvcSample
new { controller = "Home" });
builder.UseRouter(routes);
}
}
}

View File

@ -0,0 +1,3 @@

@Url.Action("About")

View File

@ -1,19 +1,23 @@
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc
{
public class ActionContext
{
public ActionContext(HttpContext httpContext, IDictionary<string, object> routeValues, ActionDescriptor actionDescriptor)
public ActionContext(HttpContext httpContext, IRouter router, IDictionary<string, object> routeValues, ActionDescriptor actionDescriptor)
{
HttpContext = httpContext;
Router = router;
RouteValues = routeValues;
ActionDescriptor = actionDescriptor;
}
public HttpContext HttpContext { get; private set; }
public IRouter Router { get; private set; }
public IDictionary<string, object> RouteValues { get; private set; }
public ActionDescriptor ActionDescriptor { get; private set; }

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Mvc
{
string locationsText = String.Join(Environment.NewLine, result.SearchedLocations);
const string message = @"The view &apos;{0}&apos; was not found. The following locations were searched:{1}.";
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, message, viewName, locationsText));
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, message, viewName, locationsText));
}
return result.View;

View File

@ -15,6 +15,8 @@ namespace Microsoft.AspNet.Mvc
public HttpContext Context { get; set; }
public IRenderUrl Url { get; set; }
public ViewData<object> ViewData { get; set; }
public dynamic ViewBag

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc
{
Action = action,
};
var actionContext = new ActionContext(context.HttpContext, context.RouteValues, action);
var actionContext = new ActionContext(context.HttpContext, null, context.RouteValues, action);
var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(actionContext);
foreach (var parameter in action.Parameters.Where(p => p.ParameterBindingInfo != null))

View File

@ -4,6 +4,7 @@ using System.Reflection;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc
{
@ -58,6 +59,15 @@ namespace Microsoft.AspNet.Mvc
{
prop.SetValue(controller, modelState);
}
else if (prop.Name == "Url" && prop.PropertyType == typeof(IRenderUrl))
{
var generator = new DefaultRenderUrl(
actionContext.HttpContext,
actionContext.Router,
actionContext.RouteValues);
prop.SetValue(controller, generator);
}
}
var method = controllerType.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("Initialize", StringComparison.OrdinalIgnoreCase));
@ -72,6 +82,5 @@ namespace Microsoft.AspNet.Mvc
method.Invoke(controller, args);
}
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc
{
public class DefaultRenderUrl : IRenderUrl
{
private readonly HttpContext _httpContext;
private readonly IRouter _router;
private readonly IDictionary<string, object> _ambientValues;
public DefaultRenderUrl(HttpContext httpContext, IRouter router, IDictionary<string, object> ambientValues)
{
_httpContext = httpContext;
_router = router;
_ambientValues = ambientValues;
}
public virtual string Action(string action, string controller, object values)
{
var valuesDictionary = new RouteValueDictionary(values);
if (action != null)
{
valuesDictionary["action"] = action;
}
if (controller != null)
{
valuesDictionary["controller"] = controller;
}
return RouteCore(valuesDictionary);
}
public virtual string Route(object values)
{
return RouteCore(new RouteValueDictionary(values));
}
protected virtual string RouteCore(IDictionary<string, object> values)
{
var context = new VirtualPathContext(_httpContext, _ambientValues, values);
var path = _router.GetVirtualPath(context);
// We need to add the host part in here, currently blocked on http abstractions support.
// The intent is to use full URLs by default.
return _httpContext.Request.PathBase + path;
}
}
}

View File

@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Mvc
return;
}
var actionContext = new ActionContext(context.HttpContext, context.Values, actionDescriptor);
var actionContext = new ActionContext(context.HttpContext, context.Router, context.Values, actionDescriptor);
var invoker = ActionInvokerFactory.CreateInvoker(actionContext);
if (invoker == null)
{

View File

@ -5,7 +5,6 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.Razor
{
@ -17,10 +16,14 @@ namespace Microsoft.AspNet.Mvc.Razor
protected TextWriter Output { get; set; }
public IRenderUrl Url { get; set; }
private string BodyContent { get; set; }
public virtual async Task RenderAsync(ViewContext context, TextWriter writer)
{
Url = context.RenderUrl;
var contentBuilder = new StringBuilder(1024);
using (var bodyWriter = new StringWriter(contentBuilder))
{

View File

@ -1,6 +1,5 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.Razor
{
@ -17,6 +16,8 @@ namespace Microsoft.AspNet.Mvc.Razor
public HtmlHelper<TModel> Html { get; set; }
public IRenderUrl RenderUrl { get; set; }
public override Task RenderAsync(ViewContext context, TextWriter writer)
{
var viewData = context.ViewData as ViewData<TModel>;

View File

@ -0,0 +1,12 @@

using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
public interface IRenderUrl
{
string Action(string action, string controller, object values);
string Route(object values);
}
}

View File

@ -0,0 +1,26 @@

namespace Microsoft.AspNet.Mvc
{
public static class RenderUrlExtension
{
public static string Action(this IRenderUrl generator)
{
return generator.Action(null, null, null);
}
public static string Action(this IRenderUrl generator, string action)
{
return generator.Action(action, null, null);
}
public static string Action(this IRenderUrl generator, string action, object values)
{
return generator.Action(action, null, values);
}
public static string Action(this IRenderUrl generator, string action, string controller)
{
return generator.Action(action, controller, null);
}
}
}