I'm trying to use Hyprlinkr to generate URL to the HTTP Post action. My controller looks like this:
public class MyController : ApiController {
[HttpPost]
public void DoSomething([FromBody]SomeDto someDto) {
...
}
}
with this route:
routes.MapHttpRoute(
name: "MyRoute",
routeTemplate: "dosomething",
defaults: new { controller = "My", action = "DoSomething" });
I expect to get a simple URL: http://example.com/dosomething, but it does not work. I tried two methods:
1) routeLinker.GetUri(c => c.DoSomething(null))
- throws NullReferenceException
2) routeLinker.GetUri(c => c.DoSomething(new SomeDto()))
- generates invalid URL:
http://example.com/dosomething?someDto=Namespace.SomeDto
Update: Issue opened at github: https://github.com/ploeh/Hyprlinkr/issues/17
I found a workaround, loosely based on Mark's answer. The idea is to go over every route parameter and remove those that have [FromBody] attribute applied to them. This way dispatcher does not need to be modified for every new controller or action.
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));
}
}