I have classes auto-generated in NetBeans with RESTful template from entities, with CRUD functions (annotated with POST, GET, PUT, DELETE). I have a problem with create method, which after inserting an entity from the frontend, I would like create to update a response so that my view will automatically (or asynchronously, if that's the right term) reflect the added entity.
I came across this (example) line of code but written in C# (of which I know nothing about):
HttpContext.Current.Response.AddHeader("Location", "api/tasks" +value.Id);
Using JAX-RS in Java, is there anyway to get the current HttpContext just like in C# and to manipulate the header?
The closest I came about is
Response.ok(entity).header("Location", "api/tasks" + value.Id);
and this one certainly is not working. It seems I need to get the current HttpContext before building the Response.
Thanks for your help.
I think you mean to do something like Response.created(createdURI).build(). This will create a response with a 201 Created status, with the createdUri being the location header value. Normally this is done with POSTs. On the client side, you can call Response.getLocation() which will return the new URI.
From the Response API
public static Response.ResponseBuilder created(URI location) - Create a new ResponseBuilder for a created resource, set the location header using the supplied value.
public abstract URI getLocation() - returns the location URI, otherwise null if not present.
Keep in mind about the location you specify to the created method:
the URI of the new resource. If a relative URI is supplied it will be converted into an absolute URI by resolving it relative to the request URI.
If you don't want to rely on static resource paths, you could get the current uri path from the UriInfo class. You could do something like
#Path("/customers")
public class CustomerResource {
#POST
#Consumes(MediaType.APPLICATION_XML)
public Response createCustomer(Customer customer, #Context UriInfo uriInfo) {
int customerId = // create customer and get the resource id
UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
uriBuilder.path(Integer.toString(customerId));
return Response.created(uriBuilder.build()).build();
}
}
This would create the location .../customers/1 (or whatever the customerId is), and send it as the response header
Note if you want to send the entity along with the response, you can just attach the entity(Object) to the method chain of the Response.ReponseBuilder
return Response.created(uriBuilder.build()).entity(newCustomer).build();
#POST
public Response addMessage(Message message, #Context UriInfo uriInfo) throws URISyntaxException
{
System.out.println(uriInfo.getAbsolutePath());
Message newmessage = messageService.addMessage(message);
String newid = String.valueOf(newmessage.getId()); //To get the id
URI uri = uriInfo.getAbsolutePathBuilder().path(newid).build();
return Response.created(uri).entity(newmessage).build();
}
Related
I am developing a servlet for JAVA EE and keep getting this error "Error Viewerpage.index method has more than one entity. You must use only one entity parameter."
#ApplicationPath("REST2")
#Path("/viewer")
public class Viewerpage extends Application {
private GlobalConfiguration globalConfiguration;
private ViewerService viewerService;
#GET
#Path(value = "/viewer")
public Response index(String filename, String page, HttpServletResponse response) throws IOException {
// set headers before we write to response body
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.TEXT_HTML);
// render a page of a file based on a parameters from request
renderPage(filename, response.getOutputStream());
// complete response
response.flushBuffer();
String value = "redirect:index";
return Response.status(Response.Status.OK).entity(value).build();
}
private void renderPage(String filename, OutputStream outputStream) {
String filepath = "storage/" + filename;
// render first page
MemoryPageStreamFactory pageStreamFactory = new MemoryPageStreamFactory(outputStream);
HtmlViewOptions viewOptions = HtmlViewOptions.forEmbeddedResources(pageStreamFactory);
Viewer viewer = new Viewer(filepath);
viewer.view(viewOptions);
viewer.close();
}
}
Any ideas what cause this error?
When you declare a resource method, you can only have one parameter that is the request entity. The parameter without any annotations is considered the entity body. All other parameters must have some kind of annotation that specifies what it is and what should be injected. If they are query parameters, use #QueryParam. If it is a path parameter, use #PathParam. If it some other non-Param injectable (that is supported) e.g. HttpServletRequest, then use #Context. Other supported "Param" injectable types are #HeaderParam, #FormParam, #CookeParam, #MatrixParam, etc.
Think of the HTTP response that gets streamed to the client. You are sending it with
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.TEXT_HTML);
renderPage(filename, response.getOutputStream());
response.flushBuffer();
But then, afterwards (when the response stream at most should be closed), you try to do something that looks like building a second response:
Response.status(Response.Status.OK).entity(value).build();
As every response can have only one set of header and body you cannot go back setting headers or sending a second response entity. That is what the error is about.
I have a front-end react application, where i make a request to a REST Jax-RS backend server.
Here is the request being sent
deletePost = (post) =>{
return deleter(config.restUrl + `posts/${post}`)
}
Here i get the standart URL for my backend, with a 'deleter' function, which is just a standardized fetch delete method (which has also worked with other entities as well).
Here is my Jax-RS resource:
#DELETE
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
#Path("/{id: [0-9]+}")
public Response deletePost(#HeaderParam("authorization") String token, #PathParam("id") Integer id) throws ResourceNotFoundException, AuthenticationException
{
AuthenticationContext authenticationContext = authenticationFacade.authenticateBearerHeader(token);
Post post = postFacade.delete(authenticationContext, id);
return Response.ok(gson.toJson(PostDTO.basic(post))).build();
}
The problem is that it gives me an error saying that the form is HTML/text:
MessageBodyWriter not found for media type\u003dtext/html, type\u003dclass com.group3.sem3exam.rest.dto.PostDTO, genericType\u003dclass com.group3.sem3exam.rest.dto.PostDTO
Since it's implying that it is the PostDTO that has the error, I went to check the basic method, which converts the entity into a Data Transfer Object, to be posted back to the client side.
public static PostDTO basic(Post post)
{
return new PostDTO(
post.getId(),
post.getContents(),
post.getCreatedAt(),
null,
ImageDTO.list(post.getImages(), ImageDTO::withoutUser)
);
}
Here it just calls the method which returns a new instance of the object.
I have not seen this error before, and I'm not sure how to handle it?
Try
return Response.status(Status.OK).entity(new Gson().toJson(PostDTO.basic(post))).build();
Imagine a post REST end point such as:
#POST
#Path("/cbo/{param1}/{param2}")
public Response updateCbo() {
//do something
return Response.status(Response.Status.OK).build();
}
My question is: if everything goes well, is it better to return the OK response or is it the default anyway? I see the GET queries are usually not bothering returning the response, just the requested content, and web clients do fetch a 200 OK header.
Thanks.
You don't have to return a response at all, let's say if you have a POST (or any other function) type call that in the response request you want to return a String (or any object for that matter if you are using a serializer like Jackson)
You can just do:
#POST
#Path("/cbo/{param1}/{param2}")
public String updateCbo() {
//do something
return "My Response"
}
Jersey will automatically return 200 for this. If you set the function to void, Jersey will automatically return 204 (Success - no content).
If you want the call to fail you can just raise an exception.
When POSTing to create a new resource, the general accepted away is to send back a 201 Created status, with the Location header set the URI of the new resource. You can see an example of one to accomplish this, in this post.
If you are just updating a resource, which rather than POST, is generally done with PUT, then instead of a 201, the general way is to send a 204 No Content, on success. Example
#PUT
#Path("/cbo/{param1}/{param2}")
public Response updateCbo(Model updated,
#PathParam("param1") String param1,
#PathParam("param2") String param2) {
Model model = modelServive.lookup(param1, param2);
if (model == null)
return Response.notFound().build();
model = PropTranferUtils.transfer(model, updated);
modelService.update(model);
return Response.noContent().build();
}
I have a Jersey 2.x endpoint that a user can POST to to update a specific property on a parent resource. On success, I would like to return a 303 status and specify the path to the parent resource in the Location header.
eg. if the user POSTed to:
http://example.com/api/v1/resource/field
then set the location header in the response to:
http://example.com/api/v1/resource
It seems like there should be a straightforward way to do this with UriInfo/UriBuilder, but I'm at a loss as to how to do it without hard-coding something that's likely to break later on.
Get the base URI (which assuming is http://example.com/api) from UriInfo.getBaseUriBuilder(), then append the XxxResource path with builder.path(XxxResource.class).
Then from the built URI, return Response.seeOther(uri).build();. Complete example:
#Path("/v1/resource")
public class Resource {
#GET
public Response getResource() {
return Response.ok("Hello Redirects!").build();
}
#POST
#Path("/field")
public Response getResource(#Context UriInfo uriInfo) {
URI resourceBaseUri = uriInfo.getBaseUriBuilder()
.path(Resource.class)
.build();
return Response.seeOther(resourceBaseUri).build();
}
}
Forgive me, but I may not be familiar with all the lingo necessary to ask this question properly.
I'm working on a fairly simple REST web service in Java using the org.apache.cxf.jaxrs.ext implementation of jax-rs. The method header is like this:
#GET
#Path("json/{fullAlias}")
#Produces({"application/json"})
public String json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req)
where MessageContext is org.apache.cxf.jaxrs.ext.MessageContext.
There are two things I'm trying to accomplish that I can't seem to figure out:
Change the content-type if certain conditions are met (e.g. for an error)
Change the status code of the response
I've tried using changing the response by accessing it through the MessageContext:
HttpServletResponse response = req.getHttpServletResponse();
response.setContentType("text/plain")
response.setStatus("HttpServletResponse.SC_BAD_REQUEST);
But these changes have no bearing on the response sent; with or without the #Produces annotation, setting the content type inside the method doesn't affect the actual content type (With the annotation, it of course returns "application/json", without it defaults to "text/html").
I am returning a simple String as the body. I've entertained trying to return a javax.ws.rs.core.Response object to do what I want, but I don't know much about it.
How would I change the content type and/or the status codes from inside this method?
One approach is to throw a WebApplicationException, as described by Pace, which will work if you are looking to specifically handle an error condition. If you are looking to be able to change your content at any time for any reason, then you will want to take a look at returning a Response as the result of your service method rather than a String. Returning a Response gives you the greatest amount of control over how your service responds to the client request (it does require more code than returning a simple string).
Here is an example of how you would can make use of the Response object:
#GET
#Path("json/{fullAlias}")
public Response json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req) {
...
if (success) {
ResponseBuilder rBuild = Response.ok(responseData, MediaType.APPLICATION_JSON);
return rBuild.build();
}
else {
ResponseBuilder rBuild = Response.status(Response.Status.BAD_REQUEST);
return rBuild.type(MediaType.TEXT_PLAIN)
.entity("error message")
.build();
}
}
I'm not sure if it's the best approach but I've done the following to solve your question #1.
public WebApplicationException createStatusException(String statusMessage) {
ResponseBuilder rb = Response.noContent();
rb = rb.type(MediaType.TEXT_PLAIN);
rb = rb.status(Status.BAD_REQUEST);
rb = rb.entity(statusMessage);
return new WebApplicationException(rb.build());
}
EDIT: I then threw the resulting WebApplicationException.
You can write your own Response Filter to change the content-type header.
#Provider
public class MimeAddingFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Content-Type", "image/png");
}
}
This filter will add the "image/png" content-type header. You can also change or remove headers in JAX-RS response filters.