I'm converting one of my existing service to become RESTful and I've got the basic things working with RestEasy. Some of my client apps should be able to execute both GET and POST requests to several services. I'm just seeking if there is any easy way around jax-rs to specify that API should accept both GETs and POSTs. Following you can find a test method, let me know if you see any way around without duplicating this in another class with #GET and #QueryParam.
#POST
#Path("/add")
public Response testREST(#FormParam("paraA") String paraA,
#FormParam("paraB") int paraB) {
return Response.status(200)
.entity("Test my input : " + paraA + ", age : " + paraB)
.build();
}
Just put your method body in another method and declare a public method for each HTTP verb:
#Controller
#Path("/foo-controller")
public class MyController {
#GET
#Path("/thing")
public Response getStuff() {
return doStuff();
}
#POST
#Path("/thing")
public Response postStuff() {
return doStuff();
}
private Response doStuff() {
// Do the stuff...
return Response.status(200)
.entity("Done")
.build();
}
}
As wikipedia says, an API is RESTful if it is a collection of resources with four defined aspects:
the base URI for the web service, such as http://example.com/resources/
the Internet media type of the data supported by the web service. This is often XML but can be any other valid Internet media type providing that it is a valid hypertext standard.
the set of operations supported by the web service using HTTP methods (e.g., GET, PUT, POST, or DELETE).
The API must be hypertext driven.
By diminishing the difference between GET and POST you're violating the third aspect.
If this scenario fits for all your resources you could create a ServletFilter which wraps the request and will return Get or Post everytime the method will be requested.
Related
I commence in REST and I have some questions:
What type must the controller return? Typically, I'm asking if my Rest #Controller must return Item object as it is or encapsulate it in ResponseEntity in order to specify http-status-code.
What http status code to use in a GET method on a particular item ("/items/2") if the given item does not exists: HttpMediaStatus.OK(200) and null return or HttpStatus.NO_CONTENT(204) and null return ?
Second part: I saw it was possible to specify #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
Context: using Spring Boot, hibernate, REST webservice.
Thank you.
Many questions in one, I'll provide short answers with a bunch of link to relevant articles and the reference documentation.
What type must the controller return?
Depends on your annotation and the RESTful-ness of your service. There are three annotations you can use for controllers: #Controller, #RestController and #RepositoryRestController.
Controller is the base annotation to mark your class as a controller. The return type of the controller endpoint methods can be many things, I invite you to read this dedicated post to get a grasp of it.
When developing a pure-REST service, you will focus on using RestController and RepositoryRestController.
RestControlleris Controller + ResponseBody. It binds the return value of the endpoint method to the web response body:
#RestController
public ItemController {
#RequestMapping("/items/{id}")
public Item getItem(#PathVariable("id") String id) {
Item item = ...
return item;
}
}
With this, when you hit http:/.../api/items/foo, Spring does its magic, automatically converting the item to a ResponseEntity with a relevant 40X status code and some default HTTP headers.
At some point, you will need more control over the status code and headers, while still benefiting from Spring Data REST's settings. That's when you will use RepositoryRestController with a ResponseEntity<Item> as return type, see the example the Spring Data REST reference.
What http status code to use in a GET method on a particular item if the given item does not exists?
Bluntly said: use HttpStatus.NOT_FOUND. You're looking for a resource that does not exist, there's something wrong.
That being said, it is completely up to you to decide how to handle missing resources in your project. If your workflow justifies it, a missing resource could be something completely acceptable that indeed returns a 20X response, though you may expect users of your API to get confused if you haven't warned them or provided some documentation (we are creatures of habits and conventions). But I'd still start with a 404 status code.
(...) #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
#Consumes and #Produces are respectively matched against content-type and accept headers from the request. It's a mean of restricting the input accepted and the output provided by your endpoint method.
Since we're talking about a REST service, communications between clients of the API and the service are expected to be JSON-formatted. Thanks to Spring HATEOAS, the answer are actually formatted with the application/hal+json content-type.
In that scenario, you can indeed not bother with those two annotations. You will need them if you develop a service that accepts different content-types (application/text, application/json, application/xml...) and provides, for instance, HTML views to users of your website and JSON or XML response to automated clients of your service.
For real life examples:
Facebook provides the Graph API for applications to read to/write from its graph, while users happily (?) surf on web pages
Google does the same with the Google Maps API
I have a situation that I need some assistance with. I have four REST URL with same path and different http methods
/api/users/** GET,POST,PUT,DELETE
I want to use Shiro to protoct the PUP, POST, DELETE and keep GET is anonymous. I configured the following URLs but with out luck
/api/users/** =rest[user:update,user:delete,user:create]
/api/users/** =anon
Maybe you could do something like this:
/api/users/**=rest[user]
Then, it kind of depends on how you are creating the REST APIs. With a JAX-RS implementation, like Jersey for example, you could do the following:
#Path("/api/users")
public class SomeResource {
#RequiresPermissions("user:read")
#GET
public Response getResource() {..}
#RequiresPermissions("user:create")
#PUT
public Response putResource() {..}
#RequiresPermissions("user:update")
#POST
public Response postResource() {..}
#RequiresPermissions("user:delete")
#DELETE
public Response deleteResource() {..}
}
This is assuming that you are going with the Annotations based authorization. You could also use the SecurityUtils.getSubject() mechanism.
Amazon's web API uses a "query API" for non-CRUD operations by using a querystring parameter to specify the operation. I want to implement non-CRUD operations in a similar manner.
Is there any way to map a POST resource method using Jersey JAX-RS dependent on the value of a particular querystring parameter? For example I would like to do something like this:
#POST #Query(name="xaction", value="move")
public Response move(#QueryParam("source") String source, #QueryParam("dest") String dest)
{
...
}
#POST #Query(name="xaction", value="copy")
public Response copy(#QueryParam("source") String source, #QueryParam("dest") String dest)
{
...
}
In the above example I would expect a framework would dispatch to the appropriate method depending on the value of the "xaction" querystring parameter regardless of URI path. Unfortunately I do not have the luxury of using paths or methods to distinguish these operations. I am open to other library suggestions that are compatible with JAX-RS or custom implementations to resolve this.
REST is not Remote Procedure Call (RPC). Even if Amazon is publishing an API like this, it is still not RESTful.
JAX-RS does not provide a mapping you describe for good reasons. Every Resource must be uniquely identifiable by the URI which includes the full path and all query parameters. URI like you describe don't identify Resources but call procedures.
Ask yourself these questions:
What are your Resources?
Do actions on Resources easily map to HTTP verbs?
If not, can actions on Resources me modeled as Resources?
As example for 3., you could thing about a MoveResource and a CopyReource.
A JSON Representation of a MoveResource could look like this:
{
"source": "/path/to/source",
"dest": "/path/to/dest"
}
Creating such a MoveResource and thus triggering a move could be done by a POST to the collection Resource at /moves. This POST would return 201 Created with a Location header like /moves/42. A GET to this Resource could return the state of the MoveResource:
{
"source": "/path/to/source",
"dest": "/path/to/dest",
"status": "success"
}
The same could be done for a CopyResource.
I have to consume a service provided by one of our partners. I was given little direction, but was told the security was to be PasswordDigest. I looked it up and immediatly saw lots of references to WSE, so off I went. It was very easy to implement and in no time I had a standard WSE user token using PasswordDigest sitting in the SOAP headers of my messages.
When we started testing today I was immediatly told (by the error message) that things weren't right. Turns out, out partner doesn't look in the SOAP header, but rather wants the security info in the http header.
I have seen lots of articles on how to add custom http headers to a proxy class, but my proxy inherits from SoapHttpClientProtocol which doesn't have a headers collection to add to. I was looking at making a raw httpWebRequest, but I have a specific method to access that has some complex parameters to deal with (and besides it feels like going backwords).
What is the best way to add custom http headers to a service proxy class that doesn't have a GetWebRequest method?
For reference:
Proxy class decleration:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.3053")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="MtomServiceSoap11", namespace="http://ws.xxxxxxx.com/")]
public partial class MtomServiceService : System.Web.Services.Protocols.SoapHttpClientProtocol {
Target method I need to call:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("uploadDocumentResponse", Namespace="http://ws.edsmtom.citizensfla.com/")]
public uploadDocumentResponse uploadDocument([System.Xml.Serialization.XmlElementAttribute(Namespace="http://ws.xxxxxxx.com/")] uploadDocumentRequest uploadDocumentRequest) {
object[] results = this.Invoke("uploadDocument", new object[] {
uploadDocumentRequest});
return ((uploadDocumentResponse)(results[0]));
}
}
The actual call to the Service is simple. The objects being pass in are not:
request.criteria = docCriteria;
request.document = document;
var result = service.uploadDocument(request);
Thanks.
It figures that 30 minutes after posting I would stumble across the answer. While the proxy class decelaration does not create a GetWebRequest method, its base class System.Web.Services.Protocols.SoapHttpClientProtocol has it and it can be overridden.
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
request.Headers.Add("blah", "blah"); // <----
return request;
}
I am trying to develop a method in my RESTful web service in Java to insert multiple entries into a MySQL DB using POST request. The generated RESTful Web Service has a method to insert a single entity, but not multiple ones. For example, it accepts:
<creature>
<sort>Mouse</sort>
<name>Pinky</name>
</creature>
But not (what I would like):
<creature>
<sort>Mouse</sort>
<name>Pinky</name>
</creature>
<creature>
<sort>Elephant</sort>
<name>Dumbo</name>
</creature>
I'm guessing that you have to loop through the entities, but not sure how to implement it, being a shameful novice.
Just ran into this myself. I need transactional posts of multiple items, so iterating on the client is out of the question. The consensus seems to be that you need to use a separate path from your normal resources:
http://chasenlehara.com/blog/creating-restful-web-services/ (Multi-resources)
RESTful way to create multiple items in one request
I couldn't find much about how to do this with Jersey, though. As it turns out, it's pretty easy. You should already have multi-entity converter and resource classes for GET requests, you just need to specify a path where the server can assume it's going to receive them:
#Path("creatures")
#Stateless
public class CreaturesResource {
...
#POST
#Consumes({"application/xml", "application/json"})
public Response post(CreatureConverter data) {
Creature entity = data.resolveEntity(em);
postCreature(entity);
}
#POST #Path("multi")
#Consumes({"application/xml", "application/json"})
public Response postMulti(CreaturesConverter data) {
Collection<Creature> entities = data.getEntities();
for (Creature c : entities) {
postCreature(c);
}
}
Then instead of posting
<creature />
to
http://.../resources/creatures
You would post
<creatures>
<creature />
<creature />
</creatures>
to
http://.../resources/creatures/multi