我正在尝试使用Hyprlinkr生成HTTP Post操作的URL。我的控制器看起来像这样:
public class MyController : ApiController {
[HttpPost]
public void DoSomething([FromBody]SomeDto someDto) {
...
}
}
用这条路线:
routes.MapHttpRoute(
name: "MyRoute",
routeTemplate: "dosomething",
defaults: new { controller = "My", action = "DoSomething" });
我希望得到一个简单的网址:http://example.com/dosomething,但它不起作用。我尝试了两种方法:
1)routeLinker.GetUri(c => c.DoSomething(null))
- 抛出NullReferenceException
2)routeLinker.GetUri(c => c.DoSomething(new SomeDto()))
- 生成无效网址:
http://example.com/dosomething?someDto=Namespace.SomeDto
更新 问题在github上开启: https://github.com/ploeh/Hyprlinkr/issues/17
答案 0 :(得分:3)
我发现了一种基于Mark's answer的解决方法。我们的想法是遍历每个路由参数并删除那些应用了[FromBody]属性的参数。这样就不需要为每个新的控制器或操作修改调度程序。
public class BodyParametersRemover : IRouteDispatcher {
private readonly IRouteDispatcher _defaultDispatcher;
public BodyParametersRemover(String routeName) {
if (routeName == null) {
throw new ArgumentNullException("routeName");
}
_defaultDispatcher = new DefaultRouteDispatcher(routeName);
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues) {
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValues.Keys) {
var parameter = method
.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null) {
if (IsFromBodyParameter(parameter)) {
routeKeysToRemove.Add(paramName);
}
}
}
foreach (var routeKeyToRemove in routeKeysToRemove) {
routeValues.Remove(routeKeyToRemove);
}
return _defaultDispatcher.Dispatch(method, routeValues);
}
private Boolean IsFromBodyParameter(ParameterInfo parameter) {
var attributes = parameter.CustomAttributes;
return attributes.Any(
ct => ct.AttributeType == typeof (FromBodyAttribute));
}
}
答案 1 :(得分:1)
第二种选择是要走的路:
routeLinker.GetUri(c => c.DoSomething(new SomeDto()))
但是,使用POST方法时,您需要删除生成的URL的模型部分。您可以使用自定义路由调度程序执行此操作:
public ModelFilterRouteDispatcher : IRouteDispatcher
{
private readonly IRouteDispatcher defaultDispatcher;
public ModelFilterRouteDispatcher()
{
this.defaultDispatcher = new DefaultRouteDispatcher("DefaultApi");
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues)
{
if (method.Method.ReflectedType == typeof(MyController))
{
var rv = new Dictionary<string, object>(routeValues);
rv.Remove("someDto");
return new Rouple("MyRoute", rv);
}
return this.defaultDispatcher.Dispatch(method, routeValues);
}
}
现在将该自定义调度程序传递到您的RouteLinker实例。
警告:我写这篇文章的时间已经很晚了,而且我没有尝试编译上面的代码,但我想我宁愿在这里尝试一下你的答案,而不是再等几天。答案 2 :(得分:1)
Dimitry的解决方案让我大部分都能到达我想要的地方,但是routeName ctor param是个问题,因为StructureMap不知道该放什么。内部hyprlink使用UrlHelper生成URI,并且想要知道要使用的路由名称
此时,我知道为什么URI生成是如此棘手,因为它与路由配置中的路由名称相关联,并且为了支持POST,我们需要将该方法与正确的路由名相关联,而不是在调度员时间知道。默认的hyprlinkr假设只有一个名为“DefaultRoute”的路由配置
我更改了Dimitry的代码如下,并采用了一种基于约定的方法,其中以“Get”开头的控制器方法映射到名为“Get”的路由,而以“Add”开头的控制器方法映射到名为“Add”的路由。添加”。
我想知道是否有更好的方法将方法与正确命名的routeConfig相关联?
public class RemoveFromBodyParamsRouteDispatcher : IRouteDispatcher
{
private static readonly ILog _log = LogManager.GetLogger(typeof (RemoveFromBodyParamsRouteDispatcher));
public Rouple Dispatch(MethodCallExpression method,
IDictionary<string, object> routeValues)
{
var methodName = method.Method.Name;
DefaultRouteDispatcher defaultDispatcher;
if (methodName.StartsWith("Get"))
defaultDispatcher = new DefaultRouteDispatcher("Get");
else if (methodName.StartsWith("Add"))
defaultDispatcher = new DefaultRouteDispatcher("Add");
else
throw new Exception("Unable to determine correct route name for method with name " + methodName);
_log.Debug("Dispatch methodName=" + methodName);
//make a copy of routeValues as contract says we should not modify
var routeValuesWithoutFromBody = new Dictionary<string, object>(routeValues);
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValuesWithoutFromBody.Keys)
{
var parameter = method.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null)
if (IsFromBodyParameter(parameter))
{
_log.Debug("Dispatch: Removing paramName=" + paramName);
routeKeysToRemove.Add(paramName);
}
}
foreach (var routeKeyToRemove in routeKeysToRemove)
routeValuesWithoutFromBody.Remove(routeKeyToRemove);
return defaultDispatcher.Dispatch(method, routeValuesWithoutFromBody);
}
private static bool IsFromBodyParameter(ParameterInfo parameter)
{
//Apparently the "inherit" argument is ignored: http://msdn.microsoft.com/en-us/library/cwtf69s6(v=vs.100).aspx
const bool msdnSaysThisArgumentIsIgnored = true;
var attributes = parameter.GetCustomAttributes(msdnSaysThisArgumentIsIgnored);
return attributes.Any(ct => ct is FromBodyAttribute);
}
}