I have a resource, in this case:
http://www.domain.com/sales-rep/{id}
Each sales representative has multiple products which they can sell. The URI for this resource would be:
http://www.domain.com/sales-rep/{id}/products
The problem is, depending on a user's permissions, you might be able to only GET or GET and PUT against this resource.
My problem is designing link relations when retrieving the sales rep resource. For example my JSON could look as follows:
{
firstname : "Dave",
lastname : "Matthews",
links : [
{ rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
{ rel : "products", href : "http://www.domain.com/sales-rep/dmat1/products" }
]
}
If I take this approach, my link relation is well named as a noun representing the resource. However it doesn't communicate to the client whether I can edit the resource or not. As far as I'm aware HTTP methods in links are not part of the spec & these are the responsibility of the documentation, so how do I communicate the permissions?
{
firstname : "Dave",
lastname : "Matthews",
links : [
{ rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
{ rel : "view-products", href : "http://www.domain.com/sales-rep/dmat1/products",
{ rel : "edit-products", href : "http://www.domain.com/sales-rep/dmat1/products" }
]
}
Something like that could work, making it easy for the client to create a menu with the correct links based on the presence of the edit-products rel. The curies for the two different rels would then link to different sections in the documentation, one for GET and PUT respectively. But does this make sense? Both approaches feel wrong to me.
short answer is yes, having the same URL behind different links comes up quite often. But what you worried about has no simple answer, it's completely based on your desired UX.
Note that view-products & edit-products are not registered link relations (see http://www.iana.org/assignments/link-relations/link-relations.xhtml ) for the list, so they should actually be URI's.
This is important because it's very common to let those URI's be dereferenceable/hence URLs which actually contain documentation about what the link relationship means. Thus your edit-products turned into a URL eg http://example.com/rels/edit-products and retrieved could result in documentation indicating what can be edited.
But i would find an edit link at this level to be rather strange. Generally there's a few things you could edit here, the product collection and the individual products in the collection. Let's focus on the latter first.
Each product is presumably a resource, so it has it's own self link. If i want to change that product as a developer it'd be natural for me to PUT some JSON representing a product to the self link. I don't need an edit link because my intent is clear with my request. If i'm unable to edit, then i should get a response indicating this (and if it's over http the appropriate http response code). If i want to remove that product I could send a DELETE to the URL of the self link. If i wanted to modify the product specifically i might use a PATCH request. If you want more restrained editing capabilities, then the edit-form IANA link relationship would be a good candidate, following this would return a form used to edit the resource as the service intends/allows. And again you could have your own URI relationship that follows whatever conventions you document. The other consideration you seem to have is you want the client to know if it can or cannot edit the resource. If you do the edit-form case then it's as easy as having or not having that relationship present. You can also use the edit IANA relationship to indicate raw editing capabilities (and it could have the same URL as self).
So i think that covers products, let's talk about the collection.
The ideas behind editing the collection are the same even if you've decided that each product isn't it's own resource. A PUT of a product to the collection's self url would imply adding a product to the collection, while a PUT of a product collection to the self url would replace the entire collection. PATCH would modify the collection. On top of that if you had interesting cases where a user could only add a product, but not remove or reorder them, than I would start dipping into custom link relationships. Again the presence of relationships could indicate to the client what can and cannot be done if you need such functionality.
I might question why you have a products collection resource at all. Instead why not have product links off of the sales rep:
{
firstname : "Dave",
lastname : "Matthews",
links : [
{ rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
{ rel : "product", href : "http://www.domain.com/product1" },
{ rel : "product", href : "http://www.domain.com/product2" },
{ rel : "product", href : "http://www.domain.com/product3" },
{ rel : "product", href : "http://www.domain.com/product4" }
]
}
Again i don't know your whole model and UX, but this would let you edit the sales rep resource to add or remove products, while products stand alone as their own resources.
Something that isn't quite clear is if you intend edit-products to return a resource used for editing the products (like a form). I often find it very useful to think of my JSON api just as i would my HTML application (web site) as web sites are very RESTful in nature. In an HTML driven application this would probably be the case. IE on the sales rep page (resource) you'd have a link to a page (resource) that allows the user to edit their products. ditto viewing products. In such a case these view product and edit products pages may be the same resource (IE it's the same page that lets you view and edit), or different resources (IE two different pages one just for editing, one just for viewing). In the former they'd probably have the same URL and in the latter they'd have different URLs. What's so cool about RESTful is that your app can change without it's semantics changing. At first you might want them to be separate pages, one for viewing one for editing, so they are different URLs, but then you might change your mind and decide one page for doing both is appropriate. now the two link relationships have the same URL but any UI built on top of these relationships still functions correctly and transitions the user to the desired resource.
There's another other option (pun) that you have and that's to submit an OPTIONS request to the self url. This should give the http methods available to the requesting user, such as PATCH, PUT, POST, DELETE, etc. from this collection of options you can construct a UI providing the right UI controls. This is unfortunately requires an extra roundtrip to the server, but is a very nice mechanism to determine the current client's allowed capabilities.
With this i bring up the most important point. In general the request method (and the request as a whole) implies the intent of the client (update, delete, add, reset, patch, whatever) and the service should interpret that intent (as well as document possible intents) and decide to fulfill the request or not. With this approach and an OPTIONS request you should generally not need to have anything but the SELF link an hierarchy relationships (eg: products) for a simple CRUD application
Related
I am developing an API in a microservice for Invoice entity that takes in input a list of Purchase Order Item (i.e. PO Item) identifiers for ex. PO# + productIdentifier together can be used to identify a POItem uniquely. The response of the API is the invoiced quantity of each PO Item.
Input Model -
input GetInvoicedQuantityForPOItemsRequest {
poItemIdentifierList : POItemIdentifierList
}
Structures
list POItemIdentifierList {
member : POItemIdentifier
}
structure POItemIdentifier {
purchaseOrderNumber : String,
productIdentifier : Long
}
Invoiced Quantity of a POItem = SUM of Quantity of Invoice Items created from that PO Item.
Note : A single PO can be used to create multiple Invoices. An Invoice can be created from multiple POs.
I am quite new to REST and so far we have been using RPC endpoints in our legacy service. But now i am building a new service where i am defining endpoints in REST format (for ex. CreateInvoice has been changed to POST /invoice) and I need some suggestions from Stack Overflow community what would be the right approach for defining the REST endpoint of this API or should we keep it in RPC format itself.
RPC endpoint for this API in legacy system : POST /getInvoicedQuantityForPOItems
Our first attempt on REST for this is : POST /invoice/items/invoicedQuantityForPOItems. But this URI does not look like a Noun it is a Verb.
this URI does not look like a Noun it is a Verb.
REST doesn't care what spelling conventions you use for your resource identifiers.
Example: this URI works exactly the same way that every other URI on the web works, even though "it looks like a verb"
https://www.merriam-webster.com/dictionary/post
The explanation is that, in HTTP, the semantics of the request are not determined by parsing the identifier, but instead by parsing the method token (GET, POST, PUT, etc).
So the machines just don't care about the spelling of the identifier (besides purely mechanical concerns, like making sure it satisfies the RFC 3986 production rules).
URI are identifiers of resources. Resources are generalizations of documents. Therefore, human beings are likely to be happier if your identifier looks like the name of a document, rather than the name of an action.
Where it gets tricky: HTTP is an application protocol whose application domain is the transfer of files over a network. The methods in HTTP are about retrieving documents and metadata (GET/HEAD) or are about modifying documents (PATCH/POST/PUT). The notion of a function, or a parameterized query, doesn't really exist in HTTP.
The usual compromise is to make the parameters part of the identifier for a document, then use a GET request to fetch the current representation of that document. On the server, you parse the identifier to obtain the arguments you need to generate the current representation of the document.
So the identifier for this might look something like
/invoicedQuantityForPOItems?purchaseOrder=12345&productIdentifiers=567,890
An application/x-www-form-urlencoded representation of key value pairs embedded in the query part of the URI is a common spelling convention on the web, primarily because that's how HTML forms work with GET actions. Other identifier conventions can certainly work, though you'll probably be happier in the long term if you stick to a convention that is easily described by a URI template.
I'm asking what is the best strategy for filtering with Symfony2.
I want to filter a table of entities (hotels). This filter should allow me to :
choose hotels with or whitout email, with or without web site etc.
choose hotels based on state and/or city (relation OneToMany)
choose what information I want to display on the table with checkboxs (for example display "email adress" on the hotel table, but do not display "tel" or "web site").
First I think to build the filter form on the HotelController. When the filter is submitted, I had a FlashBag for every $_POST sended, redirect to the same page, and if there are FlashBag I send cookies to the $reponse. Then I display the table filtered with data who are on the cookie.
But I dont't really like this, cause I had a very big indexAction() on the HotelController, and I think it'as not really clean to change $_POST to FlasBag to Cookie, is it ? I do this redirection, cause by refreshing the page, data are not posted again.
I'm also asking a question, to prevent a too big IndexAction() method, can I put some code to another method, for exemple a method PostToFlashBag() and another FlashBagToCookie(), or every method on a Controller has to end with the word "Action" and must be accessible with the router ?
Then, I think to another thing : had an entity "Filter", with every row I need. For exemple "WithEmail", "DisplayTel" etc.. Then I can build a FilterType easily, and update the Filter entitie, to redirect to the same page (again, to prevent reposting data if the user refreshes the page). Finally, I can display the table with the object Filter, with a method on the HotelRepository.
That seems great, but I'm a little worry because the filter entity will only have one entry, and I have to find the Filter(1). Due to MVC, is it correct to have a model with only one entry ?
What strategy would you choose (maybe another one) ? I'm interesting to learn good practice with MVC and Symfony2 devloppemnt.
Having a dedicated model class - let's call it Filter - that will receive the values input by the user, is definitely the way to go.
More over, use the Symfony2 form on this input, so you can have validation, and be sure that the withEmailis trully a boolean, etc. From you Filter, build you SQL/Doctrine query and return what your controller have to return, be it a view, or raw datas.
You can have any method you want in a controller. After all, controllers in Symfony2 are plain old PHP objects. They only have to implement ContainerAwareInterface. Usually they inherits Controller, but this inheritance only brings some proxy methods, like getDoctrine or render.
The only convention is that methods which are used as route must end with Action
We are creating a REST API using OpenRasta and apart from regular GET, POST, PUT and DELETE on all resources, we are also providing GET on resources with plural names. So a consumer of the API can GET, POST, PUT and DELETE on User and also perform GET on Users which will return List<Users>. Now we want the clients to be able to filter and sort it by it's properties and allow to support paging for showing data in paged tabular formats.
Although, I looked at WCF Data Services Toolkit home page and looks like it can be useful but after looking at blog posts and Getting Started page, I couldn't understand how I can use it to solve my problem in OpenRasta.
Or is there anything else simpler that I can do?
OR doesn't support stuff like OData for that functionality, mainly because it leads to very unrestful systems.
If /users is "the list of users", then it is a different resource than /users/1 (the first page of users) or /users/byName/1 (the first page of users ordered by name).
You can of course implement all this easily by registering a URI that has query parameters, as those are optional
.AtUri("/users?page={page}&filter={filter}
And your handler can look like
public List<User> Get(int page = 0, string filter = null) { ... }
I have an AREA setup in my project. I need to make the routes for the area progressive, meaning that the route will build on each other.
I'm looking at this as something like a link list. Each node in the list will have a reference to a parent. As move from left to right in the list it builds, and from right to left it removes.
In the area, I have companies and that have contacts, and child companies.
For example, I have companies that would have the following:
/Companies/list
/Company/{Id}
/Company/{id}/add
/Company/{id}/edit
/Company/{id}/delete
For the contact section I need to create the following routes:
/Company/{id}/contacts/list
/Company/{id}/contact/{id}/add
/Company/{id}/contact/{id}/edit
/Company/{id}/contact/{id}/delete
How do I make sure that /Company/{id} is always in the Contact and Child Company sections of the route?
I hope that I have made my question clear.
Subjective Generalities (take with a pinch of salt):
First off, you are using Company (singular) for companies, but then you are using contacts (plural) for the contacts. There is nothing wrong with this, from a structural point of view, but your users will thank you if you are consistent with your pluralizations. I would use the plural in both cases, but that is just my preference... it looks more like English.
You also use lower case for contacts, but upper case for Company. Doesn't look professional.
The next thing that is confusing is that you are using two {id} parameters, one for companies, one for contacts. I presume these are the ids for Company and Contacts respectively. But I am confused, but being human, I am able to deduce context unlike a computer. So you would be better of specifying the parameters in your routes. Ie:
/Companies/{CompanyId}/Contacts/{ContactId}/[action]
Answering your Question with an Example:
I get the feel you don't understand routes properly. If you did, your question would be more specific.
Your route parameters can come from a number of sources, depending on how the route is requested.
You could hard code it into a link. Or, more usefully, your route registration would be designed to catch requests that map to your Action signatures.
For example, I have an eLearning app with tutors, pupils, courses and steps (ie, the steps are like sections of a course, the pupil advances through the course step by step)
The route registration looks something like:
Route or Area Registration:
context.MapRoute(
"StepDisplay",
"Course/{CourseId}/Step/{StepOrder}/Pupil/{PupilName}/{TutorName}",
new { controller = "Course", action = "Display", TutorName = UrlParameter.Optional },
new[] { "ES.eLearningFE.Areas.Courses.Controllers" }
);
This route will catch a request from the following ActionLink:
ActionLink in View:
#Html.ActionLink(#StepTitle, MVC.Courses.Course.Actions.Display(Model.CourseId, step.StepOrder, Model.Pupil.UserName, tutorName))
Now, I just need to show you the Display action's signature:
CoursesController:
public virtual ActionResult Display(int CourseId, int StepOrder, string PupilName, string TutorName)
There are a few things to note here:
That I am able to call this specific route by giving the user a link to click on.
I construct this link using the Html.ActionLink helper
I have used David Ebbo's t4mvc nuget package so that I can specify the action I am calling and its parameters. By which I mean specifying the ActionResult parameter of the Html.ActionLink helper using:
MVC.Courses.Course.Actions.Display(Model.CourseId, step.StepOrder, Model.Pupil.UserName, tutorName)
If you think about it, what routes do is translate the url of a request into an action, so the parameters of my route are either the controller name, the action name or else they are the names of parameters in the action signature.
You can see now why naming two distinct route parameters with the same
name is such a bad idea (largely because it won't work).
So, look at your action signatures, and design your routes and your action links so that the everything marries up together.
MVC doesn't work by magic!! (Although the way it uses name conventions might lead you to believe it)
I'm making a site using Codeigniter and my URL for a particular product page is like http://www.domain.com/products/display/$category_id/$product_id/$offset
$offset is used for limiting the number of pages shown per page when using the Codeigniter's Pagination library.
How I want to make it such that my URL is something more human friendly, like http://www.domain.com/$category_name/$product_name/$offset ie. http://www.domain.com/weapons/proton-canon/3
Can anyone point me the general approach? I just started learning codeigniter and is working on my first project
You can use what's generally known as a URL slug to achieve this.
Add a new field to your table, called "url_slug" or similar. Now you will need to create a slug for each product, and store it in this field.
CI has a function in the URL helper - url_title($string) which will take a string, and convert it for use in URL's.
For example My product name would become my_product_name.
Now, in your method, you can either - keep the product_id intact, use this as a parameter for your method to show specific products, and use the slug for human friendly links, or you can just use the url_slug to refer to products.
Your URL may look like:
www.domain.com/$category_name/$product_id/my_cool_product/$offset
or it could look like
www.domain.com/$category_name/my_cool_product/$offset
with no ID. the choice is yours, but the url_slug may change - the ID won't. Which may have SEO impacts.
Regardless, your method needs to look something like:
function display_product($product_id, $url_slug, $offset) {
// do what you gotta do...
}
You can then use URL's like the above.
You will need to use URI routing as well, as the example above will attempt to look for a controller called $category_name and a method called my_cool_product, which will of course not exist.
See URI Routing for further info.