I have an issue with naming endpoints in a REST api.
Let's say you have a UI on the client side and in that UI is a table with a list of files. When tapping on a file it will proceed to download that selected one from the server. Additionally there is a button that when clicked will download all the files or selected files.
So the endpoints on the API may be structured like so...
[GET] Api/Files/{fileName}
Gets a single file by the file name provided in the route.
[GET] Api/Files
Gets a list of the files, including: FileName, Size, Type, etc...
[GET] Api/Files
Gets the files, returned as a ZIP file.
As you can see the issue is the conflict of endpoints with Api/Files. I would expect both endpoints to do what I have specified that they do. But one of them needs to change... I've thought about adding something to the end but mostly verbs come to mind. Any ideas on how the formatting could be done?
Going over the different answers and testing them out, I think the best answer is just having a different endpoint name. So I've now gone for...
[GET] Api/Files/{fileName}
Gets a single file by the file name provided in the route.
[GET] Api/Files
Gets a list of the files, including: FileName, Size, Type, etc...
[GET] Api/Files/Archive
Gets the files, returned as a ZIP file.
It's not perfect, but it makes sense.
An alternative could be...
[GET] Api/Files/Zip
But I think this doesn't work very well. As endpoints should never change and I may want to change it from a zip at some point...
The HTTP/RESTy way is to specify the response type you want with the Accept header. The endpoint can return the results as JSON if Accept is application/json and a ZIP file if it's application/zip
In the worst case, you can inspect a request's Accept header and return either a JSON result or create a ZIP file and return it with return File(...).
The Produces attribute can be used to specify the content type returned by each action, allowing you to write different actions for each content type.
Another option is to create a custom output formatter and have ASP.NET itself handle the generation of the ZIP file when the Accept header requests it. This blog post shows how to create an Excel Output formatter which returns a List of items as an Excel file when the Accept header requests this
I would expect both endpoints to do what I have specified that they do. But one of them needs to change...
Right - expanding on that idea, you have three resources (the contents of the file, the list of available files, the downloadable archive of files), but only two names; so you need at least one more name.
Good news: REST doesn't care what spelling conventions you use for your resource identifiers, so you don't actually need a good name.
/Api/0d660ac6-d067-42c1-b23b-daaaf946efc0
That will work just fine. The machines don't care.
Human beings do care though; it will be a lot easier to review an access log, or find things in documentation, if we aren't trying to guess what the different UUIDs mean.
mostly verbs come to mind
Verbs are fine. Notice that these URI all work exactly like you would expect:
https://www.merriam-webster.com/dictionary/get
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/post
The HTTP/RESTy way is to specify the response type you want with the Accept header.
Probably not what you want, here. The effective target uri is the primary cache key; when we invalidate a cached entry, all of the representations will be invalidated. If that's not the relationship you want between your list of files and your downloadable zip, then having them share a resource identifier is going to be unhappy.
Accept makes more sense when you have multiple representations of the same thing (ie, the list of files, but represented as HTML, JSON, text, XML, etc).
You might also consider what these identifiers look like in the access logs; should the list of files and the zip be logged using the same URI? Do you want general purpose log parsing tools (like alarm systems) to consider the two different kinds of fetch to be equivalent?
Related
I'm writing RESTful APIs and am getting used to the recommended protocols for using HTTP verbs for different operations.
However, I'm not sure how those protocols handle the case where you are deleting a potentially long list of items.
It appears that, like GET, the DELETE verb has no body and so is limited to the length of a URL. So how could you support accepting an arbitrarily long list of items to be deleted?
From the top....
HTTP is our standard for self-descriptive messages, which is subject to the uniform interface constraint. That in turn means that everyone on the web understands HTTP requests the same way.
In other words
DELETE /api/users/5b45eda8-067c-42c1-ae1b-e0f82ad736d6
has the same meaning as
DELETE /www/home.html
In both cases, we're asking the server to enact a change to its resource model.
Because everyone understands these requests the same way, we can create general purpose components (ex: caches) that understand the meaning of messages in the transfer of documents over a network domain and can therefore do intelligent things (like invalidating previously cached responses).
And we can do this even though the general purpose components know nothing about the semantics of the resource, and nothing about the underlying domain model hidden behind the resource.
DELETE, in HTTP, always specifies a single target URI; "bulk delete" is not an option here.
(I haven't found any registered HTTP methods that describe a bulk delete to general purpose components. It's possible that one of the WebDAV methods could express those semantics, but the WebDAV standard also has a lot of other baggage - I wouldn't try repurposing those methods for a "normal" API.)
So if you are trying to DELETE three resources in your API, then you are going to need three requests to do it -- just like you would if you were trying to DELETE three pages on your web site.
That said, if deleting a bunch of resources on your web site using a single HTTP request is more important than letting general purpose components understand what is going on: you have the option of using POST
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” -- Fielding, 2009
General purpose components will understand that the resource identified by the target URI is changing in some way, but it won't understand what is happening in the payload.
In theory, you could standardize a payload that means "we're deleting all of these resources", and then general purpose components could be implemented to recognize that standard. In practice, good luck.
Now, if instead what you want is a bulk delete of entities in your domain model, you have some options available.
On the web, we would normally use something like a form - perhaps with a check box for each entity. You select the entities that you want to delete, submit the form, and the HTTP request handler parses the message, then forwards the information to your domain model.
You could achieve something similar with a remote authoring idiom - here's a resource whose representation is a list of entities. You PUT to the server a copy of that document with entities removed, and then on the server you make changes to the domain model to match.
It's a very declarative approach: "change the domain model so that the representation of the resource will look like this".
This is analogous to how you would use HTTP to fix a spelling error in a web page: send a PUT request with the new HTML (including the spelling correction) in the request body.
PATCH is very much the same idea: we describe changes to the representation of the resource, and the server passes that information to the domain model. The difference here being that instead of sending the entire representation, we just send a patch document that describes the correction.
If you want an imperative approach - just use POST
POST /Bob
Content-Type: text/plain
Bob,
Please delete domain entities 1, 2, 5, 7
General purpose components won't understand how you are trying to modify the target resource, but they'll at least know that much.
Where things get messy is when there are lots of resources whose representation depends on the same resources. The standards don't offer us much in the way of affordances to announce "here are all the resources that have changed".
Cache invalidation is one of the two hard problems. HTTP has some affordances that work in the simple cases, but trade offs become necessary when things get more complicated.
I'm creating an API which would allow users to create, update and delete blog posts. I've got to the point where I want to now create an update handler for the blog post but creating a model for this call got me thinking..
For a PUT request should the id of the blog being updated be within the URI or be within the body like so:
In the URI:
/api/blog/23
In the body:
{
"id": "23",
"title": "new blog title"
}
Is there a right and wrong? If not, then what is generally the most followed convention for RESTful api's?
Technically there is no right or wrong way to design an API. However, a well designed API will be much easier to understand.
Assuming this is a REST API, my preference is to stay consistent. If your API requires an ID within the URI to GET resources then keeping things consistent with an ID in the URI to PUT would be my recommendation.
Microsoft has a good API design guidance article which also recommends to keep URIs consistent.
PUT, in HTTP, means something very specific
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.
It's a request that asks the server to change the servers copy to match the representation provided by the client. Think "Save", or "Overwrite" -- a content management action.
So if the JSON representation of this blog post should be an id, a title, and nothing else -- then that would be fine.
If you intention is to change the title, while leaving the rest of the representation unchanged, then you either need to (a) send the entire representation, including your edit or (b) choose a method with different semantics (POST or PATCH could make sense).
The URI is an identifier -- think key in a hashtable/dictionary. There's no particular reason that data encoded into the identifier needs to match data in the representation. It certainly can -- we'll often encode into the URI information that the server will use in its own internal implementation -- but /4ca7fce6-efce-42d1-8fc6-666c3cae4f90 is a perfectly valid identifier for a resource.
Some context:
Testing for 200 regression use cases for web API/WCF service is real pain and I want to automate it.
I am creating a tool which will automate "before my change" and "after my change" result comparison for given web api or wcf request and I should be able to ignore some tags for comparison which will be unique in both response such as response time, request execution time.
So far I am able to create two folder before and after, before contains response for all the use cases in json/xml format before my change and after contains response after my change for same operation or Web API.Files in folder are saved based on use case name.
after before
useCase1.xml useCase1.xml
useCase2.xml useCase2.xml
useCase3.xml useCase3.xml
In code I have two object for one given file in json format(I read xml and converted it to json as there is possibility that folder may contain json responses). I compared both objects and inside a loop ignoring the configured keys such as response time and request execution time.I wrote use case name in a text file if objects doesn't match for human evaluation.
So far I just had to compare all the test for the same operation or Web API, so json object structure of useCase1 from before folder and after folder was same (it differs only when I add new attributes in my change in which case i'll run comparison ignoring the new attributes to see i have not broke any historical stuff)
Question:
I also want to compare response from one endpoint of service with another on conditional basis to check that both the service are in sync when some feature get delivered.(this scenario is only "after change" scenario and user will have to provide two files which need to be compared)
Example:
AccountDetail.xml
<User>
<Account1>
<food>10</food>
</Account1>
<Account1>
<rent>20</rent>
</Account1>
<Account2>
<food>10</food>
</Account2>
<Account2>
<rent>30</rent>
</Account2>
</User>
AccountSummary.xml
<User>
<Summary>
<Account1>30</Account1>
<Account2>40</Account2>
</Summary>
</User>
Now I want to ask some input from user, so they can tell program to add all amount from Account1 from AccountDetail.xml and compare it with Summary/Account1.
Seems like I have to take SQL query kind of input to achieve above but I have no idea what to do and looking for help.So far whatever I have done is in c#, so I'll be happy if suggestions are for c# but I am open to anything which I can leverage. This is internal tool for me and my colleague and not for production.
Even if you think my entire approach is incorrect,let me know how can I correct or improve it.
I have some doubts here, I assume - "You are running repetitive test cases as part of regression test"
In such case, as I understand you should use same test case as well as data entries for validation. That way you shield your repetitive test runs from any changes (may be in logic getting tested and data as well). As a result of this you will be in a state to compare your OLD and NEW results of the test run.
Moreover, if you use same data for repetitive testing you are well aware about the asserts to be verified for making the test as pass or fail.
I'm trying to figure out how to choose between two different (identically designed) Resources files in code. All of the examples I can find online are in reference to having different language specific Resource files which are chosen based on setting the culture value. That won't work for my probelm.
This is a web service which returns an image from one of several different image repository systems. Depending on a parameter passed in to the method, the service will need to access an image repository system in order to pull the image being requested. When accessing the image repository, there are a bunch of "magic string" GUID values that represent different IDs for various lookups in that system. One of the purposes of this service is to hide all of that complexity from the user. Rather than hard-code these GUIDs into the code, I have put them into a Resources file.
The problem is this: Each different image repository system has the same set of magic string IDs that need to be used. However, the actual GUID values for these magic strings are different depending on which repository you are connecting to. For example, there is a value called "GroupIDPrompt" which might be "8a48f393642a753f0164418b670a7cdf" on one system, but "63aa28c3637b58680163b25f7e5a5d96" on a different system. In code, I'd like to refer to this value as just "Resources.GroupIDPrompt" or something similar, but I need to be able to set which Resources file will be used at runtime, based on what the consumer of the service sent me.
Normally, I might solve a problem like this by using an interface, and instantiating a specific implementation of that interface based on the request. There are two reasons that doesn't work here - #1, Resource code files are generated automatically, and if I edit them to make them inherit from an interface, this will get broken everytime the file is regenerated. #2, All resource values are created to be static members, and interfaces aren't allowed to declare static members.
I could throw the Resources files away and instead build a class to expose these values, but that means re-introducing magic hard-coded strings to my code. That isn't too terrible, I suppose, but the Resource editor is really quite handy for managing and editing these values.
How do I configure an IgnoreRoute to ignore all files with a certain extension, regardless of what directory they're in?
If I do this, everything works and my Flash movie gets played:
routes.Ignore("community/123/plan/456/video/moviename.flv");
So the "123" and "456" sections are variable and can be any integer number. Obviously though, I don't want to do one of these for each movie NOR do I have a need to replace 123 and 456 with variable placeholders. This is only an example of one type of directory, there are Flash movies stored throughout the application so what I need is an IgnoreRoute value that will ignore files that have a .flv extension no matter where in the hierarchy they are.
I've tried the following:
routes.IgnoreRoute("{file}.flv");
routes.IgnoreRoute("(.*).flv(.*)"); // Yeah I know, I'm horrible at RegEx
The only thing I can get to work so far is specifically passing the full relative path to the FLV file. Any suggestions?
Check this article by Phil Haack: http://haacked.com/archive/2008/07/14/make-routing-ignore-requests-for-a-file-extension.aspx
Long story short, we didn’t want routing to attempt to route requests
for static files such as images. Unfortunately, this caused us a
headache when we remembered that many features of ASP.NET make
requests for .axd files which do not exist on disk.
To fix this, we included a new extension method on RouteCollection,
IgnoreRoute, that creates a Route mapped to the StopRoutingHandler
route handler (class that implements IRouteHandler). Effectively, any
request that matches an “ignore route” will be ignored by routing and
normal ASP.NET handling will occur based on existing http handler
mappings.
Hence in our default template, you’ll notice we have the following
route defined.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
This handles the standard .axd requests.
........
We only allow one catch-all route and it must happen at the end of the
URL. However, you can take the following approach. In this example, I
added the following two routes.
routes.IgnoreRoute("{*allaspx}", new {allaspx=#".*\.aspx(/.*)?"});
routes.IgnoreRoute("{*favicon}", new {favicon=#"(.*/)?favicon.ico(/.*)?"});
What I’m doing here is a
technique Eilon showed me which is to map all URLs to these routes,
but then restrict which routes to ignore via the constraints
dictionary. So in this case, these routes will match (and thus ignore)
all requests for favicon.ico (no matter which directory) as well as
requests for a .aspx file. Since we told routing to ignore these
requests, normal ASP.NET processing of these requests will occur.