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.
Related
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.
I am using this Route filter
[Route("search/{*segments}")]
this takes all the segments I am providing which could be many.
Here is an example
http://localhost:50877/search/c_50_showcases%5E-displays/a_brand-name:33113319_balt:1623762%7Cmooreco:1672386/a_total-number-of-shelves:33111115429_5:3138:lt/so_ts
Now I also need the query string with this route but I am unable to make it work.
http://localhost:50877/search/?query=HP%20DesignJet&items=HEW51645A|ELI75220
It gives me 403 error.
The Web server is configured to not list the contents of this directory
How can I make a route that can take wildcard and query string to handle the incoming request. I am bound to use search in Route.
Also I tried with this
http://localhost:50877/search/test?query=HP%20DesignJet&items=HEW51645A|ELI75220
It works but this effects the SEO.
Defining action and route this way:
[System.Web.Mvc.Route("search/{*segments}")]
public ActionResult Search(string segments, string query, string items)
allows to get wildcard (in segments variable) and also query string parameters (query and items)
I am porting a legacy application to Symfony2 and I am struggling because routing doesn't include query string parameters. Some quick examples: Suppose you have a search page for books where you can filter results based on criteria:
http://www.bookstore.com/books?author=Stephen+King&maxPrice=20
The nice thing about query string parameters in a case like this is you can have any number of filters, use the ones you want for any given request, and not crowd the URL with filters you're not using.
Let's say we rewrote the routing for the above query using the Symfony2 routing component. It might look like this:
http://www.mybookstore.com/book/any_title/stephen%20king/any_release_date/max_price_20/any_min_price/any_format/any_category
Even not taking into account how arbitrarily long an unclean that URL is I still don't think it is as intuitive because each 'segment' of that route is not a key value pair but instead just a value (e.g. author=Stephen+King > /stephen%20king/).
You can of course access query string parameters in the controller by passing the Request object into the action method (e.g. indexAction(Request $request) {) but then validating them and passing them into other parts of the application becomes a hassle (i.e. where I find myself now). What if you are using the Knp Menu Bundle to build your sidebar and you want parts to be marked as .current based on query string parameters? There is no functionality for that, just functionality to integrate with Symfony2 routes.
And how to validate that your query string parameters are acceptable? I am currently looking at validating them like a form to then pass them into the database to generate a query. Maybe this is the way the Symfony2 team envisioned handling them? If so I'd just really like to know why. It feels like I'm fighting the application.
I ended up actually asking Fabien this question at Symfony Live San Francisco 2012. There was another talk at the same conference in regards to this question and I will share with you the slides here:
http://www.slideshare.net/Wombert/phpjp-urls-rest#btnNext
Basically in the slides you can see that the author agrees with me in that query string parameters should be used for filtering. What they should not be used for is determining a content source. So a route should point to a product (or whatever) and query string parameters should then be used in your controller to apply whatever logic you require to filter that content source (as per Fabien).
I ended up creating an entity in my application that I bind all my query string parameters to and then manipulate, much the same way forms are handled. In fact when you think about it it's basically the same thing.
Like in Symfony1, query strings are independent from the route parameters.
If you have a path defined as #Route("/page/{id}", name="single_page"), you can create a path in your view like this:
{{ path('single_page', { id: 3, foo: "bar" }) }}
The resulting URL will be /page/3?foo=bar.
I'm just starting out learning ASP.NET MVC 3. I've been going through the Music store sample on the ASP.NET website, as well as starting to develop my own site, but I'm having some trouble understanding how I should setup my controller action methods.
On my site each user that is logged in will be able to perform standard CRUD operations on their Projects. I've set this up similar to the Music Store sample with the following action methods on a ProjectController. So far this makes sense to me.
ActionMethod View
ProjectController.Index() Lists the active users's projects
ProjectController.Details(int id) Shows details for project 123
ProjectController.Create() Shows a form to edit a new project
ProjectController.Create(FormCollection col) Adds a new project with the form contents
ProjectController.Edit() Shows a form to edit a new project
ProjectController.Edit(int id, FormCollection col) Adds a new project with the form contents
ProjectController.Deiete(int id) Shows a delete confirmation form
ProjectController.Delete(int id, FormCollection col) Deletes a project with the provided id.
In addition, users will be able to add Items to each project. Items can not exist on their own and must be associated to a project when being created. What I'm having trouble understanding is how to pass along the reference to the project an Item should be created in. For example in my item controller I have a pair of Create() action methods similar to the controller above.
ItemController.Create() Shows a form to create a new item
ItemController.Create(FormCollection col) Creates a new item with the details from the form.
Yet I don't understand how the first Create() method passes a reference to the project which the new Item should be created in since the View() helper method can only accept one object parameter. Should I just add a reference to a project to a property of the ViewBag? I'm new to dynamic types as well and the ViewBag just seems magic to me at the point. So I'm a little hesitant to use it. I've also always thought strongly typed design is better. So should I create a separate "NewItemData" model object that contains a reference to a new Item as well as the project it is being added to?
Once the form knows which project it is adding an item to how should it pass this information back when submitted? Should there be a hidden "ProjectID" field in the form? Or should the form POST back to a URL with the project id in the query string?
www.mysite.com/Item/Create?ProjectID=1234
Finally, I also want to be able to list the items that are added to each project. Should this be part of the ItemController or the ProjectController. For simplicities sake I'm sticking with the default Controller\Action[ID] URL routing. A few of my ideas are listed below. I'm leaning towards the last option, but would really like to hear what others with more experience with this stuff think.
Action Method URL
ItemController.Index(int ProjectID) \Item?ProjectID=1234
ItemController.List(int id) \Item\List\1234
ProjectController.Items(int id) \Project\Items\1234
To answer your last question, it depends. Do Items in your model exist independently of a project? If the answer is no, then I would tend to do
ProjectController.AddItem(int id)
ProjectController.Items(int id)
where id represents the projectID.
The name of the parameters you use in the action signature directly correspond to values from the routedata and request values. {controller}/{action}/{id} is a pattern that uses the braced names as keys in the route dictionary. If you wanted you could change the routes to be {controller}/{action}/{projectid} for that action and your method signature could be the (int projectid) signature.
I don't recommend you do this just to get awesome signatures. Use comments instead if you think people will get confused.
Without changing the route pattern, if you would prefer your urls to be /project/items?projectid=3 than /project/items/3 then the action would be this:
ProjectController.Items(int projectId)
I like the prettier urls, so i'd be more apt to use the id version. That being said, if Items do not exist independently of the Project object, I would be be more likely to do this. However, if you are going to have a TON of different actions that can be performed on an Item, it would make sense to separate them into ItemController and ProjectController.
It comes down, to a large extent, about what makes sense for your application and how many actions you think a controller should have on it.
In this case you'd create a hidden field with the name=projectId and then in your Create controller have an action method.
[HttpPost]
public ActionResult Create( int projectId, FormCollection postData )
Usually you'd also use a strongly typed viewmodel so instead the FormCollection parameter use:
[HttpPost]
public ActionResult Create( int projectId, Item or ItemViewModel postData )
And as long as the name attributes match the properties in Item or ItemViewModel MVC's ModelBinder will take care of hydrating those values.
If this is a duplicate, please let me know - because i had a quick look and couldn't find anything that really answers my question.
I'm experimenting with ASP.NET MVC 2. Now coming from a Web Forms background, i only really dealt with HTTP GET and HTTP POST.
I'm trying to see how i could apply GET/PUT/POST/DELETE to the respective CRUD operations on my repository (Find, Insert, Update, Remove).
Say i have a single controller, with the following action methods:
[HttpGet] // GET: /Products/{id}
[ActionName("Products")
public ActionResult Get(int id = 0) { ... }
[HttpPut] // PUT: /Products
[ActionName("Products")
public ActionResult Put(Product product) { ... }
[HttpPost] // POST: /Products/{product}
[ActionName("Products")
public ActionResult Post(Product product) { ... }
[HttpDelete] // DELETE: /Products/{product}
[ActionName("Products")
public ActionResult Delete(Product product) { .. }
Couple of questions on that - is that how you would name/separate the action methods? Should i be passing through the entire model (Product), or just the id?
The problem i'm having is i'm not sure how to handle invoking these action methods with the relevant HTTP Verb in my View.
At this stage, I'm thinking i would have 3 Views:
"Index" - bind to IEnumerable<Product> model, listing all products, with "Edit", "Details" and "Delete" links
"Single" - bind to single Product model, listing all details for a product, with an "Update" button.
"New" - bind to single Product model, with form for creating product, with an "Create" button.
So - my question is, how do i specify i want to invoke a particular controller method with a specific HTTP Verb?
With Html.BeginForm, you can specify a FormMethod enumeration - but it only has GET and POST.
How can i perform a PUT and DELETE command?
Will i need a seperate View for each HTTP Verb?
If i have a link called "Delete", can i invoke a HTTP DELETE command to my controller, or does it need to be redirected to a new View with the form action delete?
Or, is this a silly/overkill design in the first place, should i just stick with "GET" and "POST"?
I'm (fairly) new to this style of web development (REST), so please be kind. :)
UPDATE
So i came across this interesting article from Stephen Walther, regarding this very topic.
He indicates a HTML Form only supports GET and POST (because i'm new to REST-style web development, i did not even know this, to which i am partially ashamed).
Apparently the only way to invoke a Controller action with PUT/DELETE is to do it with AJAX. Seriously?
So what should i do here, should i stick with GET/POST, or should i create a JavaScript file which wraps the underlying XmlHttpRequest code behind a nice function?
Which way are ASP.NET MVC developers leaning? Surely someone has asked themselves this same question.
The reason that you're mostly familiar with GET and POST is because a regular old browser can only GET or POST a URL, especially when submitting a form.
Setting up your controllers this way, you're looking at something more along the lines of a REST API vs. a web site at least in spirit.
And yes, you are correct that only AJAX requests can set their request methods explicitly, so you're good to go if you'll be performing actions like deleting products via AJAX (in which case I would suggest only passing the ID since it is much lighter than serializing the entire product and you'll likely have easy access to the ID in your markup).
Here's the thing. If you are building a modern web application you're probably using AJAX and without getting too dogmatic you should expect your users to have JavaScript. If you want to do CRUD without AJAX then you can always POST a Product or use GET for a delete method with the ID in the URL (caveat to that, make sure the Delete method is protected behind authorization or a web crawler will gladly consume your GETs and delete all your data...)
If you don't want to build a REST API then don't sweat the fact that you're not using DELETE (just use GET with the ID in the URL) or you're not using PUT (just use POST with the product entity in the POST body).
REST is a great style for building APIs (no presentation tier, data sent in raw format to clients who consume them, could be a phone or a website, etc.) over HTTP. HTTP is great on its own for building web pages. Use what you need for what you need it for.
If you want to build a REST API to be used by both other people AND your web site, then do that, and just have your site's controller action methods call your API methods directly. I do that all the time. Use a web client like Hammock to make it easier on you.
Personally I stick to as simple as possible full requests, instead of going AJAX all over. I use ajax, but in specific places where it enhances the experience (and I do with a progressive javascript approach).
Because of the above, I always just use GET and POST.
As for the naming, for those I'd do: Get, Update, Create, Delete, which makes it pretty clear. I actually use more like an action name that's related to what the operation does, but for simple things those work fine.
HtmlHelper.HttpMethodOverride Method (System.Web.Mvc)
How about this?