Url.Action returning incorrect URL for webapi action with Route attrubute
I have a problem with the behaviour of Url.Action();
I have a webapi where all controllers require explicit route prefix attribute and all actions require a route attribute.
I register my routes in the WebApiConfig.cs
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
I have currently commented out the line below, but (because) it did not change the incorrect behaviour:
//config.Routes.MapHttpRoute(name: "DefaultApi",
//routeTemplate: "api/v{version:apiVersion}/{controller}/{action}/{id}", defaults: new {id = RouteParameter.Optional});
My controllers look as follows:
[RoutePrefix("api/v{version:apiVersion}/programs")]
public class ProgramsController : ApiController
{
[HttpGet, Route("{telemetryKey}/versions/latest")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
}
I expect that '@Url.Action("GetLatestVersionInfo", "Programs", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })'
should return /api/v1/programs/43808405-afca-4abb-a92a-519489d62290/versions/latest
however, I get /Programs/GetLatestVersionInfo?telemetryKey=43808405-afca-4abb-a92a-519489d62290
instead. So, my routeprefix and route attributes are ignored.
Swagger correctly discovers my routes and I can validate that requests to the expected routes work OK - it's only the Url.Action() that is confused.
What can be wrong...?
asp.net asp.net-web-api url-routing asp.net-mvc-routing
add a comment |
I have a problem with the behaviour of Url.Action();
I have a webapi where all controllers require explicit route prefix attribute and all actions require a route attribute.
I register my routes in the WebApiConfig.cs
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
I have currently commented out the line below, but (because) it did not change the incorrect behaviour:
//config.Routes.MapHttpRoute(name: "DefaultApi",
//routeTemplate: "api/v{version:apiVersion}/{controller}/{action}/{id}", defaults: new {id = RouteParameter.Optional});
My controllers look as follows:
[RoutePrefix("api/v{version:apiVersion}/programs")]
public class ProgramsController : ApiController
{
[HttpGet, Route("{telemetryKey}/versions/latest")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
}
I expect that '@Url.Action("GetLatestVersionInfo", "Programs", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })'
should return /api/v1/programs/43808405-afca-4abb-a92a-519489d62290/versions/latest
however, I get /Programs/GetLatestVersionInfo?telemetryKey=43808405-afca-4abb-a92a-519489d62290
instead. So, my routeprefix and route attributes are ignored.
Swagger correctly discovers my routes and I can validate that requests to the expected routes work OK - it's only the Url.Action() that is confused.
What can be wrong...?
asp.net asp.net-web-api url-routing asp.net-mvc-routing
add a comment |
I have a problem with the behaviour of Url.Action();
I have a webapi where all controllers require explicit route prefix attribute and all actions require a route attribute.
I register my routes in the WebApiConfig.cs
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
I have currently commented out the line below, but (because) it did not change the incorrect behaviour:
//config.Routes.MapHttpRoute(name: "DefaultApi",
//routeTemplate: "api/v{version:apiVersion}/{controller}/{action}/{id}", defaults: new {id = RouteParameter.Optional});
My controllers look as follows:
[RoutePrefix("api/v{version:apiVersion}/programs")]
public class ProgramsController : ApiController
{
[HttpGet, Route("{telemetryKey}/versions/latest")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
}
I expect that '@Url.Action("GetLatestVersionInfo", "Programs", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })'
should return /api/v1/programs/43808405-afca-4abb-a92a-519489d62290/versions/latest
however, I get /Programs/GetLatestVersionInfo?telemetryKey=43808405-afca-4abb-a92a-519489d62290
instead. So, my routeprefix and route attributes are ignored.
Swagger correctly discovers my routes and I can validate that requests to the expected routes work OK - it's only the Url.Action() that is confused.
What can be wrong...?
asp.net asp.net-web-api url-routing asp.net-mvc-routing
I have a problem with the behaviour of Url.Action();
I have a webapi where all controllers require explicit route prefix attribute and all actions require a route attribute.
I register my routes in the WebApiConfig.cs
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
I have currently commented out the line below, but (because) it did not change the incorrect behaviour:
//config.Routes.MapHttpRoute(name: "DefaultApi",
//routeTemplate: "api/v{version:apiVersion}/{controller}/{action}/{id}", defaults: new {id = RouteParameter.Optional});
My controllers look as follows:
[RoutePrefix("api/v{version:apiVersion}/programs")]
public class ProgramsController : ApiController
{
[HttpGet, Route("{telemetryKey}/versions/latest")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
}
I expect that '@Url.Action("GetLatestVersionInfo", "Programs", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })'
should return /api/v1/programs/43808405-afca-4abb-a92a-519489d62290/versions/latest
however, I get /Programs/GetLatestVersionInfo?telemetryKey=43808405-afca-4abb-a92a-519489d62290
instead. So, my routeprefix and route attributes are ignored.
Swagger correctly discovers my routes and I can validate that requests to the expected routes work OK - it's only the Url.Action() that is confused.
What can be wrong...?
asp.net asp.net-web-api url-routing asp.net-mvc-routing
asp.net asp.net-web-api url-routing asp.net-mvc-routing
asked Dec 30 '18 at 14:37
BartoszBartosz
1,16921541
1,16921541
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
Try the following:
Name your route:
[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
Use Url.Link method:
@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use@Url.Route
.@Url.Link
will give you absolute url.
– Mohsin Mehmood
Dec 30 '18 at 21:27
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
add a comment |
Well, it seems there were a few things wrong.
Wrong helper:
I should be using the Url.HttpRouteUrl for generating API links from a razor view (Url.Link is for generating link from within API controllers)
Conflict with aspnet-api-versioning library
For some reason (perhaps a bug?) the prefix that I have on the controller (apiVersion
variable) breaks the URL helper mechanism.
For now, I have ditched the aspnet-api-versioning library, but created an issue on their github repo, in case its a bug.
Since I really hate the idea of creating and maintaing magic strings, so I took the following approach - each controller has a public static class which contains const values for the route names:
[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
[HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}
public static class Routes
{
public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
}
}
Now that can be used from a razor controller in a simple and relatively safe manner:
@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})
A bit better than magic strings. I've also added a bunch of unit tests for controllers where I validate that each route is unique and proper and that the routes class only contains routes for the action it contains.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53978511%2furl-action-returning-incorrect-url-for-webapi-action-with-route-attrubute%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Try the following:
Name your route:
[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
Use Url.Link method:
@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use@Url.Route
.@Url.Link
will give you absolute url.
– Mohsin Mehmood
Dec 30 '18 at 21:27
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
add a comment |
Try the following:
Name your route:
[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
Use Url.Link method:
@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use@Url.Route
.@Url.Link
will give you absolute url.
– Mohsin Mehmood
Dec 30 '18 at 21:27
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
add a comment |
Try the following:
Name your route:
[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
Use Url.Link method:
@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })
Try the following:
Name your route:
[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
{
// serious business logic
}
Use Url.Link method:
@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })
answered Dec 30 '18 at 21:13
Mohsin MehmoodMohsin Mehmood
2,2212513
2,2212513
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use@Url.Route
.@Url.Link
will give you absolute url.
– Mohsin Mehmood
Dec 30 '18 at 21:27
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
add a comment |
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use@Url.Route
.@Url.Link
will give you absolute url.
– Mohsin Mehmood
Dec 30 '18 at 21:27
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I have 100 actions on all controllers... does this means I need to create 100 named routes and maintain those 100 magic strings with route names?
– Bartosz
Dec 30 '18 at 21:21
I believe so. Also, if you want to get relative url then use
@Url.Route
. @Url.Link
will give you absolute url.– Mohsin Mehmood
Dec 30 '18 at 21:27
I believe so. Also, if you want to get relative url then use
@Url.Route
. @Url.Link
will give you absolute url.– Mohsin Mehmood
Dec 30 '18 at 21:27
1
1
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
You can check the code of UrlHelper class at github github.com/aspnet/AspNetWebStack/blob/master/src/…
– Mohsin Mehmood
Dec 30 '18 at 21:28
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
Actually, I tried the approach with the named route, but it returns an empty string.
– Bartosz
Dec 31 '18 at 8:37
add a comment |
Well, it seems there were a few things wrong.
Wrong helper:
I should be using the Url.HttpRouteUrl for generating API links from a razor view (Url.Link is for generating link from within API controllers)
Conflict with aspnet-api-versioning library
For some reason (perhaps a bug?) the prefix that I have on the controller (apiVersion
variable) breaks the URL helper mechanism.
For now, I have ditched the aspnet-api-versioning library, but created an issue on their github repo, in case its a bug.
Since I really hate the idea of creating and maintaing magic strings, so I took the following approach - each controller has a public static class which contains const values for the route names:
[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
[HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}
public static class Routes
{
public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
}
}
Now that can be used from a razor controller in a simple and relatively safe manner:
@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})
A bit better than magic strings. I've also added a bunch of unit tests for controllers where I validate that each route is unique and proper and that the routes class only contains routes for the action it contains.
add a comment |
Well, it seems there were a few things wrong.
Wrong helper:
I should be using the Url.HttpRouteUrl for generating API links from a razor view (Url.Link is for generating link from within API controllers)
Conflict with aspnet-api-versioning library
For some reason (perhaps a bug?) the prefix that I have on the controller (apiVersion
variable) breaks the URL helper mechanism.
For now, I have ditched the aspnet-api-versioning library, but created an issue on their github repo, in case its a bug.
Since I really hate the idea of creating and maintaing magic strings, so I took the following approach - each controller has a public static class which contains const values for the route names:
[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
[HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}
public static class Routes
{
public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
}
}
Now that can be used from a razor controller in a simple and relatively safe manner:
@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})
A bit better than magic strings. I've also added a bunch of unit tests for controllers where I validate that each route is unique and proper and that the routes class only contains routes for the action it contains.
add a comment |
Well, it seems there were a few things wrong.
Wrong helper:
I should be using the Url.HttpRouteUrl for generating API links from a razor view (Url.Link is for generating link from within API controllers)
Conflict with aspnet-api-versioning library
For some reason (perhaps a bug?) the prefix that I have on the controller (apiVersion
variable) breaks the URL helper mechanism.
For now, I have ditched the aspnet-api-versioning library, but created an issue on their github repo, in case its a bug.
Since I really hate the idea of creating and maintaing magic strings, so I took the following approach - each controller has a public static class which contains const values for the route names:
[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
[HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}
public static class Routes
{
public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
}
}
Now that can be used from a razor controller in a simple and relatively safe manner:
@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})
A bit better than magic strings. I've also added a bunch of unit tests for controllers where I validate that each route is unique and proper and that the routes class only contains routes for the action it contains.
Well, it seems there were a few things wrong.
Wrong helper:
I should be using the Url.HttpRouteUrl for generating API links from a razor view (Url.Link is for generating link from within API controllers)
Conflict with aspnet-api-versioning library
For some reason (perhaps a bug?) the prefix that I have on the controller (apiVersion
variable) breaks the URL helper mechanism.
For now, I have ditched the aspnet-api-versioning library, but created an issue on their github repo, in case its a bug.
Since I really hate the idea of creating and maintaing magic strings, so I took the following approach - each controller has a public static class which contains const values for the route names:
[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
[HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}
public static class Routes
{
public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
}
}
Now that can be used from a razor controller in a simple and relatively safe manner:
@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})
A bit better than magic strings. I've also added a bunch of unit tests for controllers where I validate that each route is unique and proper and that the routes class only contains routes for the action it contains.
answered Jan 2 at 13:43
BartoszBartosz
1,16921541
1,16921541
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53978511%2furl-action-returning-incorrect-url-for-webapi-action-with-route-attrubute%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown