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();
}
}
Related
I an creating an endpoint with spring boot...i can upload image to folder and save it via postman everythink works good.
i have a problem with get method when i am adding the value #RequestMapping value = "getImage/{imageName:.+}" in postman i add http://localhost:8080/api/images/getImage/{burger+png}
is that corect ???
#RequestMapping(value = "api/images")
public class ImageController {
#Autowired
public ImageService imageService;
#PostMapping(value ="upload")
public ResponseEntity uploadImage(#RequestParam MultipartFile file){
return this.imageService.uploadToLocalFileSystem(file);
}
#GetMapping(
value = "getImage/{imageName:.+}",
produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_GIF_VALUE,MediaType.IMAGE_PNG_VALUE}
)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable(name = "imageName") String fileName) throws IOException {
return this.imageService.getImageWithMediaType(fileName);
}
}
what should be the correct request url ???
It seems like it's reaching the backend fine, but failing to find path. Usually API endpoints end with parameters with a slug or query param. You can try either of the following to see if it works:
http://localhost:8080/api/images/getImage/burger.png
http://localhost:8080/api/images/getImage?imageName=burger.png
Keep in mind, you want to make sure that file exists at the path it's mentioning at the very top of the trace in the JSON response. This may depend on how you uploaded the file and with what name.
Tackling the problem of versioning an API that wasn't versioned previously, I'm running into an issue: I've got two resources set on the same URL endpoint:
#Path("/api/foo/")
public class FooResourceOne {
#GET
public Response getFoo () {
return Response.ok("Hello from One").build();
}
}
#Path("/api/foo/")
#Consumes("application/vnd.example.v2+json")
public class FooResourceTwo {
#GET
public Response getFoo () {
return Response.ok("Hello from Two").build();
}
}
The issue is, if you send a GET request with a Content-Type header (e.g. Content-Type: application/json) it goes to FooResourceOne for handling. And if you specify Content-Type: application/vnd.example.v2+json, it goes to FooResourceTwo properly. That's all well and good.
However, if you don't specify a Content-Type header at all, it goes to FooResourceTwo, which is not correct?
Not having a #Consumes annotation means "accepts any", but it seems "any" doesn't include "not specified"? Or rather "not specified" means handled by whomever (and load ordering is important)?
Is there a way to specify FooResourceOne is the Resource that should respond when no Content-Type header is given at all?
I managed a workaround that doesn't fix the underlying issue, but at least alleviates the symptom. The Resteasy Filter and PreProcessInterceptor classes are able to detect when a request comes in with no Content-Type header, but can't then do anything about it other than throw an error. So, my workaround is for FooResourceTwo to first check and see if there is no header set:
#Path("/api/foo/")
public class FooResourceOne {
#GET
public Response getFoo () {
return Response.ok("Hello from One").build();
}
}
#Path("/api/foo/")
#Consumes("application/vnd.example.v2+json")
public class FooResourceTwo {
#Inject
private FooResourceOne oldResource;
#GET
public Response getFoo (#Context HttpServletRequest request) {
if (request.getHeader("content-type") == null) {
// No, no, Resteasy; go this way...
return oldResource.getFoo();
}
return Response.ok("Hello from Two").build();
}
}
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();
}
I want to forward a REST request to another server.
I use JAX-RS with Jersey and Tomcat. I tried it with setting the See Other response and adding a Location header, but it's not real forward.
If I use:
request.getRequestDispatcher(url).forward(request, response);
I get:
java.lang.StackOverflowError: If the url is a relative path
java.lang.IllegalArgumentException: Path http://website.com does not start with a / character (I think the forward is only legal in the same servlet context).
How can I forward a request?
Forward
The RequestDispatcher allows you to forward a request from a servlet to another resource on the same server. See this answer for more details.
You can use the JAX-RS Client API and make your resource class play as a proxy to forward a request to a remote server:
#Path("/foo")
public class FooResource {
private Client client;
#PostConstruct
public void init() {
this.client = ClientBuilder.newClient();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response myMethod() {
String entity = client.target("http://example.org")
.path("foo").request()
.post(Entity.json(null), String.class);
return Response.ok(entity).build();
}
#PreDestroy
public void destroy() {
this.client.close();
}
}
Redirect
If a redirect suits you, you can use the Response API:
Response.seeOther(URI): Used in the redirect-after-POST (aka POST/redirect/GET) pattern.
Response.temporaryRedirect(URI): Used for temporary redirection.
See the example:
#Path("/foo")
public class FooResource {
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response myMethod() {
URI uri = // Create your URI
return Response.temporaryRedirect(uri).build();
}
}
It may be worth it to mention that UriInfo can be injected in your resource classes or methods to get some useful information, such as the base URI and the absolute path of the request.
#Context
UriInfo uriInfo;
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.