I have the following method in my controller, using C# MVC with Attribute roouting:
[Route("")]
[Route("/something-else")]
public IActionResult Index(){
}
Im using two different routes to access this functionallity, since i want customers with bookmarks to the previous implementation to work.
The problem is that i cant specify which of these Routes will be the default one when i issue the action like this:
<a asp-controller="FOO" asp-action="Index">
Everythiing works as expected, both URLs work, but i cant specify which of these routes will be used when navigated to by the action, via the action above.
I would like that the first route be used everytime i navigate to this action, except when someone explicitly writes the old url into the browser.
Are there any default attributes to the [Route("")] tag?
The RouteAttribute class has an Order property. From the docs:
Gets the route order. The order determines the order of route execution. Routes with a lower order value are tried first.
For example:
[Route("/something-else", Order = 1)]
[Route("", Order = 2)]
public IActionResult Index(){
}
As an aside, I would strongly discourage you from serving the same page with multiple URLs. Google's indexing will give you worse ranking because of it. Instead, consider returning a redirect to the new URL instead.
Related
I want to apply asp.net api-versioning to my web app (which didn't have versioning). However, the tricky issue is that I must ensure that APIs should work both with and without the api-version.
[ApiVersion("1.0")]
[Route("api/products/{productId}/[controller]")]
[Route("api/v{version:apiVersion}/products/{productId}/[controller]")]
[ValidateModel]
[Produces("application/json")]
public partial class ProductController : ControllerBase {
internal const string GetLatestRoute = "GET Product/GetLatestAsync";
[HttpGet(Name = GetLatestRoute)]
public async Task<IActionResult> GetLatestAsync() {
}
}
I have a controller with multiple actions, each of them is defined with a unique route name. When I add two routes (with and without versions) to the controller, there comes a route-name conflict error:
Attribute routes with the same name 'GET Products/GetLatestAsync' must have the same template:
Action: 'Service.Controllers.ProductController.GetLatestAsync (ProductFD)' - Template: 'api/products/{productId}/Product'
Action: 'Service.Controllers.ProductController.GetLatestAsync (ProductFD)' - Template: 'api/v{version:apiVersion}/products/{productId}/Product'
There are several answers on StackOverflow that say the issue can be solved by removing the route names defined for the action methods. However, in my scenario, the route names are used to create Url Links in several places in the project.
Is there an approach that I can get rid of the issue? I'm wondering whether I could append version to the route name variable or mapping the non-version api to the version/1.0 ...? On the other hand, there is a rare case that I update all the methods in a controller. So is it possible that I only define a route-prefix on the top-level of the controller and only apply the api-version on the method-level?
Route names and the route table are not API version aware. In order for this to work, you need to use double route registration like you have because you are versioning by URL segment (not recommended). If clients are properly following the links returned by the server, then always using the route generated with the explicit version in it will do. If the client doesn't honor that and just calls the APIs directly without the API version, the second template will handle that for you. If you are only generating links with the same controller, then I would suggest using CreatedAtAction instead because it will not rely on the route name. If memory serves me correct, you can specify the order of each [Route] for precedence. If unspecified, it will be the first attribute specified - which matters.
You'll also need to enable:
services.AddApiVersioning(options => options.AssumeDefaultVersionWhenUnspecified = true);
If you haven't already.
Last, but not least, beware the known, breaking change: Async suffix trimmed from controller action names. This has snared many people.
I am trying to change the view that on of my controllers points to however this is not picking up the amendment. The following is the code I am trying to use;
// GET: UserDemographics/Create
public ActionResult Create()
{
return View("Manage");
}
And my views folders look like below:
My understanding is that View tries to check for the same name as the method but I may be wrong. Any help would be appreciated.
Nothing wrong in your code. This should work perfectly
Your URL should be like this,
localhost:[Port Number]/userdemographics/Create
If you already hosted your project in a real domain, then you can replace localhost:[Port Number] with your domain name. For example,
www.example.com/userdemographics/Create
The view name specified within the View() method does not alter the path in the URL when making a request, it simply changes the name of the view that is rendered as a result of the request.
If you want the path to your action to be /UserDemographics/Manage, you would need to change the name of your Action method to Manage.
public class UserDemographics : Controller
{
// Requested by /UserDemographics/Manage in the browser
public ActionResult Manage()
{
// You don't HAVE to specify the view name here, Manage.cshtml will be rendered based on the convention.
// However as mentioned this is best practice to specify the name.
return View("Manage");
}
}
This makes it so that there is no longer a need to specify the View name as it will be automatically selected from the Views folder by convention for that controller.
I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.
I'm creating an API that needs to have the the following ability: (Example only)
GetALLProducts(int pageNumber) - with paging (page size is static)
Then, I need the other route for Getting a Product by ID
GetProduct(int productID)
the problem here is this is the same signature, so the route that should work for getting a product by id is also the same route that gets all products but with a pagenumber. The route that gets executed is the GetProduct.
Example: www.url.com/api/Products/2 - Does this get the second page of all products? or gets product id number 2?
I thought about adding pageSize to the signature, but I want to make this a system static value.
What do you think is the best resolution and clean solution here? I saw this question: WebApi Multiple actions were found with GetAll() and GetByIds(int[] ids) and this could work as well. Thoughts?
Thanks!!
Pass pagenumber as a query string. You should separate call that will be routed accordingly - by convention /products?page=1 and /product/2, respectively. Your specified route will be able to distinguish the two. Oh and my preference is to have attributed routing as suggested in the thread.
I'm converting a legacy webforms app to MVC, working through it a page at a time. To make the project easier to work with I've moved all the webforms pages, which were previously in the route of the project into a /webforms subdirectory. So I need to capture any incoming requests for /page.aspx?param=123 and redirect them to /webforms/page.aspx?param=123. I thought an easy way to do this would be to setup a route handler that passed any such requests to a controller that does that job. I set up a route like so:
routes.MapRoute("WebformsRedirect", "{*page}",
new { controller = "Webforms", action = "ForwardToPage" },
new { page = #"\S+.aspx\S*" }
);
This kind of works but it doesn't capture the query string, only the page part. I can get the query string for the Request object in the controller so it's not a huge deal but it would be nice to be able to do it through the route only. My routing unit tests (which I copied from Steve Sanderson's MVC book) actually pass correctly when I test them with querystrings so I'm confused why it isn't working. Is my regular expression wrong? They aren't my strong point.
QueryStrings are not part of the routing
if you requested for example "Home/Index?foo=bar" and you have a route that match "Foo/Bar" to Controller Foo , Action Bar without any more routing info (don't know anything about foo) you still can write
class HomeController: Controller {
ActionResult Index(string foo) {
}
}
now foo variable will equal bar , why ?
because its the model binder that gets the value of the parameters passed.
the model binder check 4 repositories by default QueryString , Routing Place Holders ,FormsCollections and Files
so what i am trying to say , the route and QueryStrings are two different things , it doesn't need to capture it