HttpServletRequest getParameterMap - Fetch the form parameters alone - java

getParameterMap() of HttpServletRequest returns both the query params and posted form data.
I am able to fetch the query parameters alone from UriInfo.getQueryParameters().
But I need the form parameters separately as a MultivaluedMap similar to query parameters, is there a way to fetch it?
EDITED:
I apologize for not making this clear. I am trying to fetch the form parameters in the filter/interceptor.

You can put the MultivaluedMap as a parameter in the resource method. This will be the body of the request. JAX-RS will put all the parameters in the map for you.
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(#Context UriInfo uriInfo, MultivaluedMap params) {}
UPDATE (to edited post)
So if you want to get the parameters in a filter, you can get the body from the ContainerRequestContext. With Jersey, instead of getting the InputStream with context.getEntityStream(), you can cast the ContainerRequestContext to Jersey's ContainerRequest implementation. This will give you access to the methods bufferEntity() and readEntity(). These methods will allow you do easily get the form parameters. You will need to buffer the entity so that it can be read later when it needs to be passed on to your resource method.
#Provider
public class MyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext context) throws IOException {
if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, context.getMediaType())) { {
return;
}
ContainerRequest request = (ContainerRequest) context;
request.bufferEntity();
Form form = request.readEntity(Form.class);
MultivaluedMap params<String, String> = form.asMap();
MultivaluedMap<String, String> query = context.getUriInfo().getQueryParameters();
}
}
If you want to use the filter only with specific resource methods, then you can use Name Binding or Dynamic Binding.
If for some reason the readEntity() returns an empty map (I've seen rare occurrences of people having this problem), you can try to retrieve the Form through an internal property
Object formProperty = request.getProperty(InternalServerProperties.FORM_DECODED_PROPERTY);
if (formProperty != null) {
Form for = (Form) formProperty;
}

Related

How to get Authorization header from an MethodInterpcetor on micronaut?

Before everything I tried this two solution but didn't work for me
Equivalent of javax.ws.rs NameBinding in Micronaut?
https://blogs.ashrithgn.com/custom-annotation-to-handle-authorisation-in-micronaut-aop-tutorial/
In my application I have to get a string in the Authorization header and then decode it from base64 and the json transform it into a POJO. Certainly the string is a jwt and I need to decode the public part of the json to get a data from a field.
Technically speaking a client will forward the header to me to take it, decode it and extract the data. (It's very bad practice but that's what I have to do).
For this I am using micronaut 2.4.1 and this is my code:
Interceptor:
public class HeadInterceptor implements MethodInterceptor<Object, Object> {
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
Request request = (Request) context.getParameterValueMap().get("request");
// Where do i get Authorization header?
// i.e String token = (String) context.getParameterValueMap().get("Authorization");
String token = "eyJhdWQiOiJ0ZXN0IiwiaXNzIjoidGVzdCIsInN1YiI6InRlc3QiLCJleHAiOjExMTExMTEsImlhdCI6MTExMTExMTEsImRhdGEiOiJ0ZXN0In0=";
ObjectMapper mapper = new ObjectMapper();
Info info = mapper.readValue(new String(Base64.getDecoder().decode(token)), Info.class);
request.setData(info.getSub().toUpperCase());
return context.proceed();
}
}
Controller:
#Controller("/main")
public class MainController {
#Post
#Head
public Single<Response> index(#Body #Valid Request request) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
}
Here's a sample app https://github.com/j1cs/micronaut-jacksonxml-error
(ignore the name is for other issue)
In your implementation, the header cannot be shown in the interceptor because your index method doesn't receive it as a parameter.
So, if you add it as a parameter as below:
...
#Post
#Head
public Single<Response> index(#Body #Valid Request request, #Header("Authorization") String authorizationHeader) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
...
Then, you can retrieve it in the intercept method via getParameterValues(). Basically, it will be the second argument.
...
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
...
String token = (String) context.getParameterValues()[1];
...
}
...
Update
Since you want your Request to contain both body and header, I edited the solution a bit. Basically, the header is added as a member variable to Request as below:
public class Request {
#NotNull
#NotBlank
private String info;
private String data;
#Header("Authorization")
String authorizationHeader;
}
Then, use #RequestBean rather than a #Body annotation on your Request parameter:
...
#Post
#Head
public Single<Response> index(#RequestBean #Valid Request request) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
...
Finally, you can access the header easily in your intercept() method as follows:
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
...
Request request = (Request) context.getParameterValueMap().get("request");
String token = request.authorizationHeader;
...
}
I created a pull request for this change here, so you can check how it works.
In order to address the problem, you may first break the problem into parts.
Part 1: How to get arbitrary header (or list all headers)?
Try to use request.getHeaders() doc.
Part 2: How to get the header named Authorization ?
Use the way in part 1. In addition, be careful about the case. For example, is Authorization the same as authorization?
Method 2:
In controller (https://github.com/j1cs/micronaut-jacksonxml-error/blob/master/src/main/java/me/jics/MainController.java):
public Single<Response> index(#Body Request request, #Header('Authorization') String authorization) {
...
}
p.s. the "Header" annotation's doc is here: https://docs.micronaut.io/2.0.1/api/io/micronaut/http/annotation/Header.html
In interceptor:
...
String token = context.getParameterValueMap().get("authorization");
...
Why the code looks like this:
Firstly get the auth header you want using parameter injection.
Secondly, recall the fundamental concepts of AOP / AspectJ (which your interceptor class uses). Inside your interceptor, you intercept a method (in your case, the index method in controller. Thus, you can happily get the parameters of that method. In the code above, just the authorization parameter.
Please tell me if you are stuck on somewhere (and paste the code and the outputs).

Can I add request parameter on Controller level and forward it to another website?

The idea is, I receive POST request with some parameters already included
e.g., car name and price
I need to add some parameter that client should not know about (for example, car's VIN, which client should not know before he makes a purchase)
Then, with parameter added, I want to forward this request to another site, which all data that I have passed (car name, price, vin)
Is there a way to implement on Controller level, not filters?
#RequestMapping(value = "/abc")
public void ABC(final HttpServletRequest request) {
request.getParameterMap().put("vin", VIN_CODE); // cannot do that because it is read-only
return "forward:https://completelyanothersite.com";
}
EDIT: Based on comments, forwarding is not solution, instead redirecting is the one
The little-less dirty (that I know of) way to do that is by extending the request:
public class WrappedHttpServletRequestWrapper extends HttpServletRequestWrapper
{
private final Map<String, String[]> modifiableParameters;
private Map<String, String[]> allParameters = null;
public WrappedHttpServletRequestWrapper (final HttpServletRequest request,
final Map<String, String[]> additionalParams)
{
super(request);
modifiableParameters = new TreeMap<String, String[]>();
modifiableParameters.putAll(additionalParams);
}
#Override
public Map<String, String[]> getParameterMap()
{
// allParameters contains old parameters + the ones you added
return Collections.unmodifiableMap(allParameters);
}
//... other overriden methods
}
And use that one to add your parameters. Here is the reference.
And here is how you will use it:
Map<String, String[]> extraParams = new TreeMap<String, String[]>()
HttpServletRequest wrappedRequest = new WrappedHttpServletRequestWrapper(request, extraParams);
request.getRequestDispatcher("url").forward(wrappedRequest, response);
Spring MVC provides a powerful set of features to automatically parse and map HTTP requests and responses for you so you don't have to do it manually and can focus on business logic. In this case, Spring can hand you a map of all request parameters like this:
#PostMapping("/abc")
public void abc(#RequestParam Map<String, String> params) { ... }
(or, if you need multi-valued parameters, you can use MultiValueMap, which is slightly more standards-correct but a bit more complicated to work with). You get a private copy of the map, which in practice is a mutable LinkedHashMap, but if you're ever unsure you can always just say new HashMap<>(params).
Once you have this params map, you can add whatever additional values you want to it, but unless you can send someone to another controller inside your own application you'll need to either generate a redirect: response (in which case the VIN will be visible in the URL) or make a REST request yourself to the other service and then relay the information back to the user.
You can send a redirect to another website, but can not forward it to another website.
Use resp.sendRedirect(url);
Refer this thread Difference between JSP forward and redirect

How to get the payload of REST api using ContainerRequestContext in filter method

I've a filter through which POST REST api goes with and i want to extract the below part of my payload in the filter.
{
"foo": "bar",
"hello": "world"
}
Filter code :-
public class PostContextFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
String transactionId = requestContext.getHeaderString("id");
// Here how to get the key value corresponding to the foo.
String fooKeyVal = requestContext. ??
}
}
I don't see any easy method to get the payload to the api using the ContainerRequestContext object.
So my question is how do i get the key value corresponding to the foo key in my payload.
Whereas filters are primarily intended to manipulate request and response parameters like HTTP headers, URIs and/or HTTP methods, interceptors are intended to manipulate entities, via manipulating entity input/output streams.
A ReaderInterceptor allows you to manipulate inbound entity streams, that is, the streams coming from the "wire". Using Jackson to parse the inbound entity stream, your interceptor could be like:
#Provider
public class CustomReaderInterceptor implements ReaderInterceptor {
// Create a Jackson ObjectMapper instance (it can be injected instead)
private ObjectMapper mapper = new ObjectMapper();
#Override
public Object aroundReadFrom(ReaderInterceptorContext context)
throws IOException, WebApplicationException {
// Parse the request entity into the Jackson tree model
JsonNode tree = mapper.readTree(context.getInputStream());
// Extract the values you need from the tree
// Proceed to the next interceptor in the chain
return context.proceed();
}
}
This answer and this answer may also be related to your question.

How to send a query params map using RESTEasy proxy client

I'm looking for a way to pass a map that contains param names and values to a GET Web Target. I am expecting RESTEasy to convert my map to a list of URL query params; however, RESTEasy throws an exception saying Caused by: javax.ws.rs.ProcessingException: RESTEASY004565: A GET request cannot have a body.
. How can I tell RESTEasy to convert this map to a URL query parameters?
This is the proxy interface:
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public interface ExampleClient {
#GET
#Path("/example/{name}")
#Produces(MediaType.APPLICATION_JSON)
Object getObject(#PathParam("name") String name, MultivaluedMap<String, String> multiValueMap);
}
This is the usage:
#Controller
public class ExampleController {
#Inject
ExampleClient exampleClient; // injected correctly by spring DI
// this runs inside a spring controller
public String action(String objectName) {
MultivaluedMap<String, String> params = new MultivaluedHashMap<>();
// in the real code I get the params and values from a DB
params.add("foo", "bar")
params.add("jar", "car")
//.. keep adding
exampleClient.getObject(objectName, params); // throws exception
}
}
After hours digging down in RESTEasy source code, I found out that there is no way to do that though interface annotation. In short, RESTEasy creates something called a 'processor' from org.jboss.resteasy.client.jaxrs.internal.proxy.processors.ProcessorFactory to map the annotation to the target URI.
However, it is really simple to solve this issue by creating a ClientRequestFilter that takes the Map from the request body (before executing the request of course), and place them inside the URI query param. Check the code below:
The filter:
#Provider
#Component // because I'm using spring boot
public class GetMessageBodyFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
if (requestContext.getEntity() instanceof Map && requestContext.getMethod().equals(HttpMethod.GET)) {
UriBuilder uriBuilder = UriBuilder.fromUri(requestContext.getUri());
Map allParam = (Map)requestContext.getEntity();
for (Object key : allParam.keySet()) {
uriBuilder.queryParam(key.toString(), allParam.get(key));
}
requestContext.setUri(uriBuilder.build());
requestContext.setEntity(null);
}
}
}
PS: I have used Map instead of MultivaluedMap for simplicity

how send result back in Spring 2.x

I have a method which returns true or false based on some parameters. So I make an ajax call (using Ext.ajax.request). In spring 2.x version how do I send back the result?
So for my controller I extend BaseSimpleCommandController and override the method
ModelAndView doExecute(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
I want to know what would i need to do to send back just a boolean value. I am little confused as what needs to be done. I now i have to send back a ModelAndView type object but nor sure i should i embed a single boolean value in to this object.
EDIT: the BaseSimpleCommandController is specific to my project which in turn extends AbstractCommandController from spring. sorry for the confusion.
If you just want to return "true" or "false" there is no need to use models, command controllers, etc. Simply inject HttpServletResponse and send the data directly:
public void handle(HttpServletResponse response) {
boolean flag = //...
response.getWriter().print(flag);
}
Request params can be mapped via annotation in the method parameter, or through the request's parameter map
Method Parameter:
#RequestMapping(method=RequestMethod.GET)
public void someCall(#RequestParam(value="param1") String paramName)
...
Where param1 would be the get parameter param1. If you don't provide a value for the annotation, it tries to bind to the name of the parameter in the method name (paramName in this case).
Parameter Map
#RequestMapping(method=RequestMethod.GET)
public void someCall(HttpServletRequest request)
{
Map<String, String[]> paramMap = request.getParameterMap();
}
Hope this helps!

Categories

Resources