Adding Attribute Route on Controller adds Parameter to Url - asp.net-core

I want to change the URL that will access my controller, but when I do, instead of getting controller/action/id, I get controller/action?id=(the id number) in the URL.
I am using default routing in my MVC .Net Core.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
On my controller I have [Route("DifferentName/{action=index}")] for attribute routing.
I have tried adding [Route("DifferentName/{action=index}/{id?}")]
but I get an exception "RoutePatternException: The route parameter name 'id' appears more than one time in the route template."

These attributes are incorrect according to the documentation:
[Route("DifferentName/{action=index}/{id?}")]
[Route("DifferentName/{action=index}")]
This is how it should look like for controller when we want to only change its part of URL:
[Route("DifferentName/[action]")]
[action]
[action] will be replaced by your action name when asp .net core will be resolving URLs.
More information about routing and can be found in the documentation

but I get an exception "RoutePatternException: The route parameter
name 'id' appears more than one time in the route template."
I have reproduced your error. I need to confirm with you whether the reason for your error is to add Route attribute to both controller and action.
If so, as the exception points out, you don't need to specify route repeatedly.
Modify the following settings to implement the url like 'Differentname/action/5':
[Route("DifferentName/{action=index}")]
public class ShowPdfController : Controller
{
public IActionResult Index()
{
return View();
}
[Route("{id?}")]
public IActionResult Action(int id)
{
return View();
}
}
Then, you can see the test result of this setting:

Related

Does ASP.NET WebAPI conventional routing works without specifying action and HTTP verb constraints?

In our classic ASP.NET WebAPI project, we could declare a route and the framework would select the correct action based on the HTTP verb in the request.
However in .NET Core WebAPI, I tried the following route configuration
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute(
name: "DefaultRoute",
pattern: "{controller}/{id?}"
);
});
My controller has one method
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public WeatherForecast Get()
{
//return weather forecast
}
}
When trying the following URL, I get 404 whereas in a similar classic ASP.NET WebAPI project it would automatically execute the Get method.
https://localhost/weatherforecast
Does that mean for conventional routing we need to add multiple routes with same pattern, with default action and HTTP method constraints for it to work properly?
This question is only about conventional routing, suggesting to switch to attribute routing is not an answer.
I found a question that tries to simulate this behavior in classic ASP.NET WebAPI in ASP.NET Core: Route action based on HTTP verb?
The example is in .NET Core 2 and MVC, but trying it in .NET Core 3 WebAPI works the same.
Seems the answer is No, in ASP.NET Core WebAPI, if the route doesn't have action in the route pattern and no HTTP method constraints, the framework won't automatically try to match with actions based on HTTP verb in the requests.
In order to achieve this, multiple routes with default actions and Verb constraints need to be added.
Routing is responsible for mapping request URL to an endpoint and it comes with two types Conventional and Attributes routing.
And from your question, you are expecting conventional routing with default rout which you can achieve it .NET CORE using below line of code.
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Search}/{action}/{id?}");
});
Note: But keep in mind that convetional routing will not work if you decorate your controller with [ApiController] attribute.
By default .NET CORE supports attribute routing so you have to prefix the route by placing [Route] attribute on a controller level. Please see below example
[Route("api/[controller]")]
[ApiController]
public class SearchController : ControllerBase
{
[HttpGet("{company}")]
public IActionResult Get(string company)
{
return Ok($"company: {company}");
}
[HttpGet("{country}/{program}")]
public IActionResult Get(string country, string program)
{
return Ok($"country: {country} program: {program}");
}
}
The above code will work as you expected (Attribute routing).
If you are decorating your controller by [ApiController] attribute then you have to use Attribute routing and any conventional routing defined in startup class will be overridden. Please see more details here.
Does that mean for conventional routing we need to add multiple routes with same pattern, with default action and HTTP method constraints for it to work properly?
Yes, in asp.net core web api, if you want to use conventional routing, you need to remove [ApiController] attribute and [Route] attribute firstly and use the following route with default action
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=WeatherForecast}/{action=Get}/{id?}");
});
Refer to Conventional Routing in ASP.NET Core API
Update:Using Url Rewriting
You could always write your own url rewrite rules to meet your requirements.Refer to below demo which deal with url like /weatherforecast:
Create Rewrite Rules:
public class RewriteRuleTest : IRule
{
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
var path = request.Path.Value;// path= "/weatherforecast" for example
if(path !=null)
{
context.HttpContext.Request.Path = path + "/" + request.Method;
// "/weatherforecast/post"
}
}
}
Startup.cs
app.UseRewriter(new RewriteOptions().Add(new RewriteRuleTest()));
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute(
name: "GetRoute",
pattern: "{controller=WeatherForecast}/{action=Get}/{id?}"
);
});

ASP Core: how to route to API Controller that is located in the area folder?

The structure:
+ MyProj
+ Areas
+ Configuration
- Pages
- ConfigurationApiController.cs
To create controller without Controllers folder was proposed by VS2017 and it is ok for me since I use Razor Pages and do not need Controllers folder:
Those doesn't work:
http://localhost:8080/api/Users
http://localhost:8080/api/GetUsers
http://localhost:8080/Configuration/api/Users
http://localhost:8080/Configuration/api/GetUsers
Controller defined:
[Route("api")]
[Produces("application/json")]
[ApiController]
public class ConfigurationApiController : ControllerBase
{
private readonly ApplicationSettings applicationSettings;
[HttpGet]
public ActionResult GetUsers()
{
Mvc routing configured standard way:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
How to route to GetUsers action of ConfigurationApiController ?
Modify the api route and add the Area Attribute to provide the area name for [area] route.
[Area("Configuration")]
[Route("[area]/api/[controller]")]
[ApiController]
public class ConfigurationApiController : ControllerBase
{
}
That's all, and it can be accessed at http://localhost:8080/Configuration/api/ConfigurationApi
Some other routing options:
Using AspNetCore.RouteAnalyzer working option found: http://localhost:8080/api (yes, without action)
After removing web APIs attributes
// [Route("api")]
// [Produces("application/json")]
// [ApiController]
then http://localhost:8080/ConfigurationApi/GetUsers
it could be ok but there is no area in the routing and it seems "routing to the area by conventions" doesn't work is asp core:
ASP Core: how to configure area for api controller without AreaAttribute (or how to enable convention area routing for Api controller)?
and https://github.com/aspnet/AspNetCore/issues/7042
Also in this case ContentResult { Content = json, ContentType = "application/json" } should be return but this is ok for me since I prefer to use in place serialization instead of stream serializers.
This routes to http://localhost:8080/Configuration/api
[Area("Configuration")]
[Route("[area]/api")]
[Produces("application/json")]
[ApiController]
other option: [Route("[area]/api/[action]")] routes to http://localhost:8080/Configuration/api/GetUsers
when removing area attribute throws the run-time error Error: While processing template '[area]/api', a replacement value for the token 'area' could not be found. Available tokens: 'action, controller'. To use a '[' or ']' as a literal string in a route or within a constraint, use '[[' or ']]' instead.
//[Area("Configuration")]
[Route("[area]/api")]
[Produces("application/json")]
[ApiController]
To support #Url.Action(action: "myArea", controller: "myControllerApi") routing should be configured manually.
Asp Core routes:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultArea",
template: "{area:exists}/{controller}/{action}"); // matches only those where area route value is defined
});
Asp Core 3 routes (startup Configure):
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "defaultArea",
pattern: "{area:exists}/{controller}/{action}");
});

How to create custom Routing in ASP.NET Core

I need help with URL rewriting. I am new to ASP.NET Core MVC. When I type anything in {param} part then routing should redirect it to my controller.
So if anyone to types in {param} like
https://mydoamin.com/{param}
then it should be redirected to this url:
https://mydoamin.com/{controller}/{action}/{actionurl}={param}
I found the answer for my question. Just define new custom route in your startup.cs file before your default route.
routes.MapRoute(
"Member", // Route name
"{actionURL}", // URL with parameters
new { controller = "Pages", action = "Details" } // Parameter defaults
);
It's working form me.
We can do the same using this approach.
I find this more convenient.
[Http("add/user/{user}/{password}")]
public IActionResult AddUser(string user, string password)
{
//do your things here...
}
I could recommend you to see this blog post from Stephen Walther: ASP.NET 5 Deep Dive: Routing
I am not sure if this works as you want. https://mydoamin.com/{controller}/{action}/{actionurl}={param} seems not to be a valid URL to me. The part {actionurl}={param} is probably the query part which is comes as a key/value pair and starts always with a ?. You could probably fix your routing if your desired URL would look like https://mydoamin.com/{controller}/{action}/?key1=value1&key2=value2
I don't know still this is useful OR not but in asp.net core 3 this worked for me like a charm.
routes.MapRoute(
"MoviesByReleaseDate",
"movies/released/{year}/{month}",
new { controller = "Movies", action = "ByReleaseDate" }
);

Asp.Net Web API action string route mapping

I have a contoller named customer and an action method for GetAllCustomers which recieves single string input parameter as FirstName. The issue is that I want the url to be like
MyAPI/Customer/MyFirstName
but the above stated URL is not working for me instead it working as below
MyAPI/Customer?firstname=MyFirstName
How can I make it work like the first url using ApplicationRouting (nuget package)? I have configured the attrbute route for the action method as below.
[GET("Customer/{firstname}")]
public List<Customer> GetAllCustomersFirstname(String firstname)
{
//code goes here
}
Edit
Here is the reference whatI am trying to achieve but it is not happening for me in ApiController
Attribute Routing Github
Open up ApiConfig in the AppStart folder. You need to add the route for that particular action. I think you'll need more than just what you showed there for route since you have to tell it which action to call and what methods to allow. E.g.
config.Routes.MapHttpRoute(
name: "yourRoute",
routeTemplate: "api/{controller}/{firstname}",
defaults: new { controller = "Customer", action = "GetAllCustomersFirstname" });

MVC2 Routing with WCF ServiceRoute: Html.ActionLink rendering incorrect links!

I have a WCF service that lives side-by-side with an MVC2 web site. I'd like for my URL for the service to look like this:
http://localhost/projdir/Service
The MVC site is in its infancy so it still has all its boilerplate controllers etc.
The following code works at first glance in global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new ServiceRoute("Service", new ServiceHostFactory(),
typeof(MyService)));
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
}
The service appears just where I described and works as advertised. Great.
However, I just noticed that ordering my code this way changes all of my ActionLink's. For example, the "About" tab on the MVC site now appears like this:
http://localhost/projdir/Service?action=About&controller=Home
This is obviously incorrect (it should be http://localhost/projdir/Home/About/ ).
If I move the ServiceRoute addition below the default MapRoute() call, then I get a missing controller error. (Actually I get a "StructureMapControllerFactory did not return an instance of a controller" error, because I'm wired up with StructureMap, duh, it's not a controller to begin with.)
Interestingly, it only seems to be affecting the output of Html.ActionLink(). I can manually type in http://localhost/projdir/Home/About/ and get to the correct page.
What horribly obvious newbie mistake am I making?
Try moving the Service route after the MVC route. But to avoid the "missing controller" error that you got before, add the MVC route with a Route Constraint. These route constraints can be Regex - basically you'd want your route constraint to be any controller that is not "Service". When a request for "Service" is requested, it will make it fall through and his the WCF Service Route.
I resolved with that:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^(?!api).*" }
);
routes.Add(new ServiceRoute("api", new DataServiceHostFactory(), typeof(dwService)));
I hope this good for you
Another solution is to inherit the ServiceRoute and override the GetVirtualPath method to return null as described here
public class AppServiceRoute : ServiceRoute
{
public AppServiceRoute(string routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
: base(routePrefix, serviceHostFactory, serviceType)
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
This way, reverse route mapping never select this route for any Action. Works like a charm