I've got an app that's basically a proxy to a service. The app itself is built on Jersey and served by Jetty. I have this Resource method:
#POST
#Path("/{default: .*}")
#Timed
#Consumes("application/x-www-form-urlencoded")
public MyView post(#Context UriInfo uriInfo, #Context HttpServletRequest request) {
...
}
A user submits a POST form. All POST requests go through this method. The UriInfo and HttpServletRequest are injected appropriately except for one detail: there seem to be no parameters. Here is my request being sent from the terminal:
POST /some/endpoint HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 15
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:8010
User-Agent: HTTPie/0.9.2
foo=bar&biz=baz
Here the POST body clearly contains 2 parameters: foo and biz. But when I try to get them in my code (request.getParameterMap) the result is a map of size 0.
How do I access these parameters or this parameter string from inside my resource method? If it matters, the implementation of HttpServletRequest that is used is org.eclipse.jetty.server.Request.
Three options
#FormParam("<param-name>") to gt individual params. Ex.
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(#FormParam("foo") String foo
#FormParam("bar") String bar) {}
Use a MultivaluedMap to get all the params
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(MultivaluedMap<String, String> formParams) {
String foo = formParams.getFirst("foo");
}
Use Form to get all the params.
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(Form form) {
MultivaluedMap<String, String> formParams = form.asMap();
String foo = formParams.getFirst("foo");
}
Use a #BeanParam along with individual #FormParams to get all the individual params inside a bean.
public class FormBean {
#FormParam("foo")
private String foo;
#FormParam("bar")
private String bar;
// getters and setters
}
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(#BeanParam FormBean form) {
}
Related
I have a Spring Boot application using jax-rs with resteasy (3.0.24). I'm trying to get the HttpHeaders for a request as such:
#DELETE
#Path("/myendpoint")
public Response myMethod(#Context HttpHeaders headers, #Context HttpServletRequest request) {
// headers is always null
}
The headers param is always null even though I'm making the request with multiple headers. As an alternative, I'm extracting them via the HttpServletRequest.getHeaderNames(), but I'd really like know why headers is not populated.
Found the (embarrassing, although I deflect the blame to the author:)) error. #Context HttpHeaders headers was using Spring's implementation and not that from jax-rs.
You gotta get the headers using the #Context then check if the one one you want is there.
#Path("/users")
public class UserService {
#GET
#Path("/get")
public Response addUser(#Context HttpHeaders headers) {
String userAgent = headers.getRequestHeader("user-agent").get(0);
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
}
I have the following REST API to parse the given JSON:
POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.TEXT_PLAIN)
#Path("/test")
public String getText(#FormDataParam("file") InputStream fileInputStream, #FormDataParam("file") FormDataContentDisposition fileDetail) throws Exception {
when I test it using the chrome extension postman, the filedetail.getName() is working however the input stream received is null. here the post request I sent :
POST /parse/test HTTP/1.1
Host: localhost:8080
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="file"; filename="1.json"
Content-Type:
----WebKitFormBoundaryE19zNvXGzXaLvS5C
The inputstream received is null .
Note: if I set the content type to "multipart/form-data" I got an exception :
java.lang.NullPointerException
com.sun.jersey.multipart.impl.MultiPartReaderClientSide.unquoteMediaTypeParameters(MultiPartReaderClientSide.java:245)
com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readMultiPart(MultiPartReaderClientSide.java:172)
com.sun.jersey.multipart.impl.MultiPartReaderServerSide.readMultiPart(MultiPartReaderServerSide.java:80)
com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:158)
com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:85)
com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:490)
com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:555)
com.sun.jersey.multipart.impl.FormDataMultiPartDispatchProvider$FormDataInjectableValuesProvider.getInjectableValues(FormDataMultiPartDispatchProvider.java:122)
com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$EntityParamInInvoker.getParams(AbstractResourceMethodDispatchProvider.java:153)
com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:183)
so I send it without any header, how can I read the file I sent from the postman, is there anything wrong with my REST API ?
do you use org.glassfish.jersey.bundle (jaxrs-ri) ?
if you do, you have to add MultiPartFeature.class to your ApplicationConfigure.java (which contains the Override of getClasses())
if you use grizzly so you have to put and register that class in ResourceConfig.
here an example for both
first grizzly
public static HttpServer startServer() {
final ResourceConfig rc = new ResourceConfig().packages(true, "your.controllers.package");
rc.register(MultiPartFeature.class);
return GrizzlyHttpServerFactory.createHttpServer(URI.create("http://localhost:8080/"),rc);
}
now the jersey
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
addResources(resources);
return resources;
}
private void addResources(Set<Class<?>> resources) {
resources.add(MultiPartFeature.class);
}
I also remove Consumes annotation from my method (I believe it detect it as multipart/form-data by default) and also remove content-type from client request because in this case, it cause error 400
I am using Spring 4 with mapped methods as follows
#RestController
#RequestMapping("/v3/users")
public class UserController {
...
#RequestMapping(value = "/{userId}/reset_password", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Void> resetPassword(
#PathVariable("userId") Long userId, #RequestBody UserPasswordResetRequestDTO data) {
// Logic here
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
}
public class UserPasswordResetRequestDTO {
private Long userId;
private String oldPassword;
private String newPassword;
// Getters and setters
}
then, i do this request:
POST /v3/users/6/reset_password HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 8afe6ef8-a4cd-fc9d-a6cc-b92766a56bd6
{"oldPassword":"actualPassword", "newPassword":"newOne"}
And all UserPasswordResetRequestDTO attributes are coming null
I've been searching and i find some related problems with the difference that those were PUT methods (then since Spring 3.1 can be done with HttpPutFormContentFilter), but this is a post and i couldn't find any problem... what i am doing wrong?
EDIT
I've tried changing #RequestBody with #ModelAttribute and it just mapped "userId" attribute (coming from url), leaving null the rest of attributes. Just the same as #RequestBody did with the difference of userId
I am really really disconcerted
Finally, i discovered what was going on. I had this jackson config
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
builder.serializationInclusion(Include.NON_NULL);
return builder;
}
which was causing the conversion from camel case to underscores on serialization and deserealization. So there was nothing wrong with code, just an invalid request, which in this case had to be this one:
POST /v3/users/6/reset_password HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 8afe6ef8-a4cd-fc9d-a6cc-b92766a56bd6
{"old_password":"actualPassword", "new_password":"newOne"}
I'm trying to get the body of a POST request by using HttpServletRequest or UriInfo. Given a class like this one (reduced for this question):
#Path("/nodes")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
public class Nodes {
public NodeResource() {
//initial stuff goes here
}
/**
* gives an empty response. For testing only!
*/
#POST
#Consumes("application/json")
#Path("{id}/test-db-requests")
public Response giveNodes(#PathParam("id") final String id, #Context HttpServletRequest request, #Context UriInfo uriInfo){
//String readReq = request.getQueryString(); //would work for GET
MultivaluedMap<String,String> readParams = uriInfo.getQueryParameters();
LOG.debug("what is readParams?", readParams); //goes, but shows nothing
if (readParams != null) {
LOG.debug("null or not?"); //goes, too
for (Map.Entry<String,List<String>> entry: readParams.entrySet()) {
List<String> values = entry.getValue();
LOG.debug("params POST key: {}", entry.getKey()); // goes not
for (String val: values) {
LOG.debug("params POST values: {}", val);
}
LOG.debug("params POST next entry:::");
}
}
List<?> results = null; //currentDBRequest(id);
List<?> content = new ArrayList<>();
if (results != null) {
content = results;
}
return Response.ok(content).build();
}
}
Instead of using
MultivaluedMap<String,String> readParams = uriInfo.getQueryParameters();
//not possible at all - for GET only!? See first comment.
I also tried to use
Map<String,String[]> readParams = request.getParameterMap();
//what is about this one?
with different following code of course. But that did not work, either.
So when I fire a simple request like /nodes/546c9abc975a54c398167306/test-db-requests with the following body
{
"hi":"hello",
"green":"tree"
}
(using an JSON Array does not change anything)
and stuff in the HEADER (some informations):
Content-Type: application/json; charset=UTF-8
Accept: application/json, text/plain, */*
Connection: keep-alive
the result is disappointing, readParams is not null, but does not contain any data. Before I start to play with getReader I wanted to ask: what am I doing wrong? Is there a problem in my POST, in my Java code or in the used HttpServletRequest method(s)? Thanks!
Related questions (where I found some possible solutions), among others:
How can I grab all query parameters in Jersey JaxRS?
How to access parameters in a RESTful POST method
Alright, Jackson would actually do this for me. Just use the argument of the method, which you want to use. (See examples below.)
But you would probably not use a POST in combination with an id parameter. POST is usually used for saving fresh resources, which do not have an id (in the DB, a primary key). Moreover the path /api/{resource_name}/{id}/{some_view} would be useful for GET. Just api/{resource_name}/{id} for a GET (single entry) or a PUT (update an existing entry).
Assume you are in a resource for Pet.class. You want to catch the POSTs for this class in order to do something special with them, based on the view test-db-requests. Then do:
#POST
#Consumes("application/json")
#Path("{id}/test-db-requests")
public Response giveNodes(final String pet, #PathParam("id") final String id){
//do stuff for POST with a strigified JSON here
}
or
#POST
#Path("{id}/test-db-requests")
public Response giveNodes(final Pet pet, #PathParam("id") final String id){
//do stuff for POST with an instance of pet here (useful for non
//polymorphic resources
}
I have following jersey method:
#POST
#Path("path")
#Produces({MediaType.APPLICATION_JSON})
public Response isSellableOnline(#QueryParam("productCodes") final List<String> productCodes,
#QueryParam("storeName") final String storeName,
#Context HttpServletRequest request) {
System.out.println(storeName);
System.out.println(productCodes.size());
...
}
in rest client I sends following data:
in console I see
null
0
What do I wrong?
You're reading the parameters from query string, which go in the form:
http://yourserver/your/service?param1=foo¶m2=bar
^ start of query string
But you're sending the parameters as part of the form.
Change the way you consume the parameters in your service:
#POST
#Path("path")
#Produces({MediaType.APPLICATION_JSON})
public Response isSellableOnline(#FormParam("productCodes") final List<String> productCodes,
#FormParam("storeName") final String storeName,
#Context HttpServletRequest request) {
System.out.println(storeName);
System.out.println(productCodes.size());
...
}