REST endpoint design says: Not use verb
In an workflow-like create Employee which has multi-tab style like "Basic Details", "Educational Details", "Work Experience", etc... One first tab data is filled continue button is pushed resulting in an backend API call which just validates the detail in that tab and returns the list of validation errors if any or moves to next tab for the user to fill data. So basically this calls for validate API for each of the tabs with no intention of saving data. Now one thing that comes naturally is below:
POST /employee/basic/validate
(removing api versioning details from endpoint for simplicity)
But using validate in API means verb. How to design then?
There's a separate flow where one can just save "basic details" of employee - so its like any normal API validate and save - so POST /employee/basic/ is good for that case.
REST endpoint design says: Not use verb
That's not a REST constraint - REST doesn't care what spellings you use for your resource identifiers.
All of these URL work, exactly the way that your browser expects them to:
https://www.merriam-webster.com/dictionary/post
https://www.merriam-webster.com/dictionary/get
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/patch
https://www.merriam-webster.com/dictionary/delete
Resources are generalizations of documents; the nature of the HTTP uniform interface is that we have a large set of documents, and a small number of messages that we can send to them.
So if you want a good resource identifier, the important thing to consider is the nature of the "document" that you are targeting with the request.
For instance, the document you are using to validate user inputs might be the validation policy; or you might instead prefer to think of that document as an index into a collection of validation reports (where we have one report available for each input).
Seems that what you try to do in the end is to run your operation in dry-run mode.
My suggestion would be to add a dry-run option as request parameter for instance.
/employee/basic?dry-run=true
REST says that you should use standards like HTTP to achieve a uniform interface. There are no URL standards as far as I know, even OData says that its URL naming conventions are optional.
Another thing that the browser is a bad REST client. REST was designed for webservices and machine to machine communication, not for the communication of browsers with webapplications, which is sort of human to machine communication. It is for solving problems like automatically order from the wholesaler to fill my webshop with new items, etc. If you check in this scenario both the REST service and REST client are on servers and have nothing to do with the browser. If you want to use REST from the browser, then it might be better to use a javascript based REST client. So using the browser with HTML forms as a REST client is something extreme.
If you have a multitab form, then it is usually collected into a session in regular webapplications until it is finalized. So one solution is having a regular webapplication, which is what you actually have, since I am pretty sure you have no idea about the mandatory REST constraints described by Fielding. In this case you just do it as you want to and forget about REST.
As of naming something that does validation I would do something like POST /employee/basic/validation and return the validation result along with 200 ok. Though most validation rules like "is it a date", "is it a number", etc. can be done on the clients currently they can be done even in HTML. You can collect the input in a session on server or client side and save it in the database after finilazing the employee description.
As of the REST way I would have a hyperlink that describes all the parameters along with their validations and let the REST client make tabs and do the REST. At the end the only time it would communicate with the REST service is when the actual POST is sent. The REST client can be in browser and collect the input into a variable or cookies or localstorage with javascript, or the REST client can be on server and collect the input into a server side session for example. As of the REST service the communication with it must be stateless, so it cannot maintain server side session, only JWT for example where all the session data is sent with every request.
If you want to save each tab in the webservice before finalizing, then your problem is something like the on that is solved with the builder design pattern in programming. In that case I would do something like POST /employeeRegistrationBuilder at the first step, and which would return a new resource something like /employeeRegistrationBuilder/1. After that I can do something like PUT/POST /employeeRegistrationBuilder/1/basics, PUT/POST /employeeRegistrationBuilder/1/education, PUT/POST /employeeRegistrationBuilder/1/workExperience, etc. and finalize it with PUT/POST /employeeRegistrationBuilder/1/finished. Though you can spare the first and the last steps and create the resource with the basics and finish it automagically after the workExperience is sent. Cancelling it would be DELETE /employeeRegistrationBuilder/1, modifying previous tabs would be PUT/PATCH /employeeRegistrationBuilder/1/basics. Removing previous tabs would be DELETE /employeeRegistrationBuilder/1/basics.
A more general approach is having a sort of transaction builder and do something like this:
POST /transactions/ {type:"multistep", method: "POST", id: "/employee/"}
-> {id: "/transactions/1", links: [...]}
PATCH /transactions/1 {append: "basics", ...}
PATCH /transactions/1 {append: "education", ...}
PATCH /transactions/1 {remove: "basics", ...}
PATCH /transactions/1 {append: "workExperience", ...}
PATCH /transactions/1 {append: "basics", ...}
...
POST /employee/ {type: "transaction", id: "/transactions/1"}
-> /employee/123
With this approach you can create a new employee both in multiple steps or in a single step depending on whether you send actual input data or a transaction reference with POST /employee.
From data protection (GDPR) perspective the transaction can be the preparation of a contract, committing the transaction can be signing the contract.
Related
I have a REST service which is a POST to create a user, if the user does not exist, the user is created, and the service returns a 200 with the user in a json format.
Case 1: What if the user exists already, do I return a functionnal exception, so a json containing an error (all of this managed by the error handling of spring boot), and what about the http status code
Some people say to send a 303 or a 409 ... many different answers, and what about the response body in that case?
Case 2: What if in the backend we have let say a rule on the name (like containing a space) which returns an error (space not allowed in a name), same questions, do i have to return a functionnal exception and what about the http status code in this case
Somehow I want the API consumer to know what kind of json structure to handle and i guess the http status code helps for that ?
It all depends on how one interprets the various http status codes and how user friendly do you want your HTTP payload responses to be. Below are few suggestions:
NEW USER CREATED : If its a new user and gets created successfully in the backend then you return http status code 201. This is a technical status code. You can also return a functional status in the response body mentioning "User created"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
USER ALREADY EXISTS : If the user already exists, you should respond with http status code 200 with a response payload body mentioning a functional status "User already exists"
USER CREATION FAILED : If the new user rules are not satisfied at the backend service and it throws an error then the http status code of 400 can be used and functional status in response payload of "User creation failed, please conform to the user name rules" https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
For an API Consumer to know everything about your API's, you may want to provide a API specification document. You may use open API spec(previously known as swagger) https://swagger.io/specification/
Somehow I want the API consumer to know what kind of json structure to handle and i guess the http status code helps for that ?
Not quite.
The HTTP status code is meta data in the transfer documents over a network domain. It communicates the overall semantics of the response (for instance, is the body of the message a representation of a resource, or a representation of an error? is this response cachable? and so on).
For unsafe requests in particular, cache invalidation is sensitive to "non-error status codes". The difference between 303 (non-error status code) and 409 (error status code) can be significant.
The Content-Type header gives you a mechanism to describe the kind (schema) of the message you are returning (ex: application/problem+json).
The way I think about it: the information for your bespoke consumer belongs in the message-body; we lift data from the message-body to the HTTP metadata so that general-purpose components can take advantage of that information (for example, by invalidating cache entries).
So we would normally start by defining the schema and semantics of the message body, and making sure that we have intelligent ways to communicate all of the things we want the caller to know. In other words, we are defining the documents that we pass to the client, and how to extract information from them.
Information that HTTP components need to know get copied from our bespoke document into the standardized forms (status code, headers).
Where things get complicated: the fact that something is an "error" in your domain, that doesn't necessarily mean that it should also be considered to be an "error" in the transfer of documents over a network domain.
A common case: we are using our API to navigate some work through a process; that process has a happy path, and also some exceptional paths that we normally try to avoid (accounts are overdrawn, items are out of stock, etc).
An HTTP request can move work from the happy path to an exception path and still be a "success" in the transfer of documents domain.
The easiest heuristic I know is to think about previously cached copies of responses by the same target URI. If those responses are still re-usable, then you are probably looking at a 4xx status code. If the responses should be invalidated, then you are probably looking at a 2xx or 3xx status code.
I am writing a custom Java webscript that accepts document noderef and an external username (string value) as parameters. I have auditing enabled and the audit log shows access to the document when I call the webscript. Now I wanted to know if it is possible to modify the audit trail so that when it shows the log for that particular document it also shows the name of the external user.
webscript url: http://localhost:8080/alfresco/service/node/{noderef}/user/{user}
On calling this I get the following output in log:
Extracted audit data:
Application: AuditApplication[ name=alfresco-access, id=1, disabledPathsId=2]
Values:
/alfresco-access/transaction/sub-actions=readContent
/alfresco-access/transaction/action=READ
/alfresco-access/transaction/node=workspace://SpacesStore/c21db432-4ad6-4af2-8bcf-78bc89724afe
/alfresco-access/transaction/type=cm:content
/alfresco-access/transaction/path=/app:company_home/app:shared/cm:audit-services-context.xml
/alfresco-access/transaction/user=admin
New Data:
/alfresco-access/transaction/sub-actions=readContent
/alfresco-access/transaction/action=READ
/alfresco-access/transaction/type=cm:content
/alfresco-access/transaction/user=admin
/alfresco-access/transaction/path=/app:company_home/app:shared/cm:audit-services-context.xml
I want to store the {user} also in the audit trail.
You can try to use AuthenticationUtil.setFullyAuthenticatedUser. I think this should help you. But I didn't test this.
You probably do not want to do that, at least not in the way you describe, not without making extra security precautions.
This goes IMHO opinion against security standards, if admin needs to read a document,the operation needs to be logged with his username, if a normal user needs to access a document, he needs to be properly authenticated for that operation.
Judging from the little context I have I would say this is actually an integration with some other app that does not share SSO with Alfresco. So I would recommend a solution of the following :
Use proper SSO between Alfresco and your application, have the concerned user ping the right endpoint in Alfresco and let SSO authenticate the request properly for you.
Use a shared secret (something like a shared passphrase to encode encode the authority name in the request + proper authentication subsystem or request filter to handle that) or a key pair (something like securecomms between solr and alfresco) to be able to securely pass on authority information to the request
Use a system account (preferably not admin, but one that is dedicated to this usecase/application integration) to generate a valid alf_ticket for the user in question, and have your app attach that ticket to the request. (Of course, your "impersonate" webscript would need to check for the right system/integration username, before running the snippet to get the alf_ticket from a runAsSystem block). In this case, I would also recommend not using the admin account for this but rather use a user with no permissions at all except for this usecase.
If you are going to opt for the quick implementation that you have, I would recommend at least the following :
You need to make sure that not any user can ping that webscript and that only admin/system user can actually access that webscript.
You probably should log the whole impersonation operation in the audit trail (either using the same audit entry or a separate one), so that it would be clear that this is actually an operation that was made on behalf of the user and not directly by the user himself
If you use the webscript in question for anything other than reading the content of the node (Can be the case also if you have a onReadContent behaviour that has some nasty AuthenticationUtil.setFullyAuthenticatedUser as well), and you require that operation to be logged as system/originally authenticated user, You will probably have a hard time doing that... and you should switch to a more robust approach!
I'm developing a program that queries and prints out open data from the local transit authority, which is returned in the form of an XML response.
Normally, when there are buses scheduled to run in the next few hours (and in other typical situations), the XML response generated by the page is handled correctly by the java.net.URLConnection.getInputStream() function, and I am able to print the individual results afterwards.
The problem is when the buses are NOT running, or when some other problem with my queries develops after it is sent to the transit authority's web server. When the authority developed their service, they came up with their own unique error response codes, which are also sent as XMLs. For example, one of these error messages might look like this:
<Error xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Code>3005</Code>
<Message>Sorry, no stop estimates found for given values.</Message>
</Error>
(This code and similar is all that I receive from the transit authority in such situations.)
However, it appears that URLConnection.getInputStream() and some of its siblings are unable to interpret this custom code as a "valid" response that I can handle and print out as an error message. Instead, they give me a more generic HTTP/1.1 404 Not Found error. This problem cascades into my program which then prints out a java.io.FileNotFoundException error pointing to the offending input stream.
My question is therefore two-fold:
1. Is there a way to retrieve, parse, and print a custom XML-formatted error code sent by a web service using the plugins that are available in Java?
2. If the above is not possible, what other tools should I use or develop to handle such custom codes as described?
URLConnection isn't up to the job of REST, in my opinion, and if you're using getInputStream, I'm almost certain you're not handling character encoding correctly.
Check out Spring's RestTemplate - it's really easy to use (just as easy as URLConnection), powerful and flexible. You will need to change the ResponseErrorHandler, because the default one will throw an exception on 404, but it looks like you want it to carry on and parse the XML in the response.
I'm looking to do the following with AJAX...
I have a request to my server that takes a considerable amount of time to complete. The request is made in the controller and upon completion a HTML page is loaded informing the user of its completion.
However, what I'd like to do is have the request sent asynchronously, load the completion page and then load the requests result once it become available. I assume I would use AJAX to do this but I'm not exactly sure how. Can anyone point me to a good guide for doing something like this?
In case my explanation above is too confusing here is what I want to do...
1) Send request to server from Controller asyncronously.
2) load HTML page.
3) When request has completed fill field in already loaded HTML page with the response from the request.
I wrote a tutorial recently that walks through how to do this with Play 1.2, JSON, and jQuery:
Tutorial: Play Framework, JPA, JSON, jQuery, & Heroku
There are 2 parts you need to take into account here:
The client side
The server side
For the client side; an Ajax request (for example, using jQuery.ajax) is per definition asynchronous. This means that you should be able to do the following - again using jQuery, which makes things easier - in your HTML page:
// The ready handler, which fires when the page has been loaded
$(function() {
jQuery.ajax(
// Do your thing here
);
});
For the server side; in case your operation is going to be running for a relatively long time on the server (for instance several web service calls or long running IO operations) you'll want to use Play's asynchronous capabilities to let the Play! server execute things as effeciently as possible. It does this by offloading the long running operation(s) to their own threads.
The only thing left to do is set-up a route to your controller, implement the handler method and render something that your client-side JavaScript code is capable of parsing (JSON is probably the easiest, using Play's renderJson()).
I haven't used this set-up myself - maybe someone can confirm this would be the way to do it?
I have a scenario that I have a button in JSP page which sends an email, the request is send to servlet asynchronously using jQuery Ajax and JSON, servlet searches in DB, if the user has an email, it returns the email address and sends an email to it, then forwards to the result page with success or fail of sending the email, but in a case that the user doesn't have an email, it returns false values using JSON to JSP and then a JSP form appears to the user to enter his email.
Is it good practice to use Ajax and I know that not each time there's a return value to the user or send request to servlet using get method which return a parameter in a case that the user doesn't have an email?
Using ajax is in practically all cases very good for User Experience. With ajax, the user will experience instant feedback without the need to face an annoying "flash of content" or a (partially) empty page because the whole HTML response needs to be generated/buffered by the server first. This is really a huge plus of using JS/ajax.
Using JSON is generally favorable above XML, HTML or even plain text. But there is no "best practice" with regard to the ajax data exchange format between client and server. Just pick whatever suits the requirement the best. JSON is perfectly fine for this case. jQuery understands it out-the-box and in Java you have choice of a plethora of easy-to-use JSON parsers.
However, when developing an ajax-enabled webapplication, you really need to take into account that the core functionality does not break when the client has JS disabled. This is called Unobtrusive JavaScript. Most of the searchbots, mobile browsers and textbased browsers don't use JS. You should try to use JS only for Progressive Enhancements. To test this yourself, in Firefox you can use for example the Web Developer Toolbar to easily enable/disable JS support. Run your website with JS disabled and observe if the core functionality is maintained as well.
The best way to achieve this is to start developing the website without any single line of JS code, even without a single onclick, onsubmit, onwhatever attribute on the HTML elements. Once you get the core functionality to work, then you can start adding JS in flavor of a script which executes during document ready and attachs functions to the HTML elements of interest (even here, you should not change the original HTML code!). Let the JS functions fire ajax requests on the same URL or maybe a different one, depending on the requirement. You can in the Servlet distinguish between an ajax and normal request as follows:
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
// Handle ajax request. Return JSON response here.
} else {
// Handle normal request. Return normal HTML response here (by JSP).
}
See also:
Simple calculator in JSP - contains unobtrusive JSP/Servlet/jQuery example
Json is just a data-interchange format. Using Json or not has nothing to do with using asynchronous communication or not... You can do both communication types using Json (or XML, or serialized objects, it doesn't matter).
Now, in your problem, it looks like you just want to use Asynchronous communication to improve the user experience (it will not flick the user's browser). If that's the case, Asynchronous communication is the way to go!
I don't think you need ot use AJAX in this.
The main idea of the ajax is to render server response without postback and in your case you are redirecting page after you get some kind of result.
In my opinion you shoul choose on of these two ways.
1) Use AJAX, send data to servlet and then render response from server wether the mail is sent or not.
2) Submit your form to servlet and sent email and then redirect to jsp with the success/fail result.
Hope it helps.