how to process variable numbers of parameters for jersey post requests - http

I've got a Jersey REST server that responds to post requests like so:
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String postHtml() {
I don't know ahead of time the names of all the parameters that might be sent to me. With a GET request I handle this like:
#Context
private UriInfo context;
#GET
#Produces(MediaType.TEXT_HTML)
public String getHtml() {
MultivaluedMap<String, String> queryParameters = context.getQueryParameters();
how can I do a similar thing with a POST request. I simply want to get all the parameters supplied in the post and I'll work with them in my code.

Turns out you can do:
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String postHtml(MultivaluedMap<String, String> inFormParams) {
If all your parameters are String type, which mine are. It would be good to know what to do if you have non-String parameters.

Related

Array in Api's Query String

Using WebClient do send an array to an ApiController via query string I get the error 400.
The api method looks like
public IHttpActionResult List([FromUri] Model model)
In the Model class I have
public int[] Ids { get; set; }
In the client side the code looks like:
webClient.QueryString.Add("ids", "1");
webClient.QueryString.Add("ids", "2");
...
await webClient.DownloadStringTaskAsync(url);
If I send just one "ids" parameter the code works fine, but not with two or more.
I found that the client creates the url like "url?ids=1,2" instead "url?ids=1&ids=2".
Is there some configuration I missed?
WebClient will automatically turn multiple values with the same key into a comma separated string. You can change this behavior, see: How to build WebClient querystring with duplicate keys?
I would recommend using HttpClient instead of WebClient though.

Serve static content in Spring Boot despite using #RequestMapping("**")

The context
I am currently working on an educational project. This implies two Spring Boot REST servers. One is an actual server, which does some processing.
The one I'm interested in is the other. It is a proxy which will redirect all calls to the first one. So that when I call http://localhost:8080/foo, my proxy server will in turn call http://localhost:8090/foo. And if the first server returns A, the proxy will return {"proxied": A, "someInformationAboutThisCall": B}.
I managed to get to this point with some probably inelegant but functioning code of which I give an excerpt below. The key here is that I use #RequestMapping("**") to achieve this. The next step is to design an interface that will make my additional information immediately legible, which is basically the point of this project. If I remove all #RequestMapping("**"), it works just fine.
The question
Now my problem is the following: having used #RequestMapping("**"), I cannot serve static content (the calls get redirect to the other REST server, which does not serve static content). How could I configure Spring Boot/Spring MVC to ignore resources available as static content when mapping the requests, or make the PathResourceResolver prioritary over my controller?` Or should I serve my static content from yet another JVM/server?
Thanks in advance for your help!
Edit of interest: while doing some tests, I discovered that the static content is served, with some restrictions, if I use #RequestMapping("*").
/index.html generates an error page (as does more generally any static content directly in public)
/itf/index.html works (as does more generally any file in public/itf or any other subdirectory of public)
/itf does not work: Spring Boot seems unaware of an index file in it. I must specify a full URI, down to the specific file I want to display.
This however does not work at all with #RequestMapping("**"), which I need.
The tentatives
I tried using a WebMvcConfigurerAdapter with an HandlerInterceptorAdapter (found on SO, SO again and many other places on the Internet), but could not start my project anymore because Spring boot then does not find the InterceptorRegistry bean (has there been recent changes in Spring Boot? I'm using the version 1.5.3.RELEASE).
I also tried some anti-matching but not only does it not work, it also feels very very dirty (and this whole project is probably not optimal, so that's saying a lot).
The code samples for the curious
My "proxy" controller
Note: you can suggest better ways to realize this in comments. Please keep in mind that, though I'm always open to enhancement suggestions, this was not my question.
#RestController
public class ProxyController {
#Value("${monitored.url.base}") // "http://localhost:8090"
private String redirectBase;
#RequestMapping(value = "**", method = {RequestMethod.POST, RequestMethod.PUT})
public ProxiedResponse proxifyRequestsWithBody(HttpServletRequest request, #RequestHeader HttpHeaders headers, #RequestBody Object body) throws URISyntaxException {
return proxifyRequest(request, headers, body);
}
#RequestMapping(value = "**")
public ProxiedResponse proxifyRequestsWithoutBody(HttpServletRequest request, #RequestHeader HttpHeaders headers) throws URISyntaxException {
return proxifyRequest(request, headers, null);
}
private ProxiedResponse proxifyRequest(HttpServletRequest request, #RequestHeader HttpHeaders headers, #RequestBody Object body) throws URISyntaxException {
final RequestEntity<Object> requestEntity = convertToRequestEntity(request, headers, body);
// call remote service
final ResponseEntity<Object> proxied = restTemplate.exchange(requestEntity, Object.class);
// Return service result + monitoring information
final ProxiedResponse response = new ProxiedResponse();
response.setProxied(proxied.getBody());
// set additional information
return response;
}
// Won't work properly for POST yet
private <T> RequestEntity<T> convertToRequestEntity(HttpServletRequest request, HttpHeaders headers, T body) throws URISyntaxException {
// Build proxied URL
final StringBuilder redirectUrl = new StringBuilder(redirectBase).append(request.getRequestURI());
final String queryString = request.getQueryString();
if (queryString != null) {
redirectUrl.append("?").append(queryString);
}
// TODO enhancement: transmit headers and request body to make this a real proxy
final HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
return new RequestEntity<>(body, headers, httpMethod, new URI(redirectUrl.toString()));
}
}
My dirty attempt at excluding static resources URLs
#Configuration // adding #EnableWebMvc did not solve the problem
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
private static class StaticResourcesHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String requestURI = request.getRequestURI();
if (requestURI == null || "/".equals(requestURI) || "/index.html".equals(requestURI) || requestURI.startsWith("/assets")) {
return super.preHandle(request, response, null);
}
return super.preHandle(request, response, handler);
}
}
#Autowired
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new StaticResourcesHandlerInterceptor()).addPathPatterns("/**");
}
}
You can split the path into a wild-card, and a named path variable which must match a negative lookahead regular expression.
#RequestMapping("/{variable:(?!static).*}/**")
You can then use #PathVariable String variable as an argument of your controller method to obtain the value of variable if you need to pass it.
(Would rather have written a comment but I have insufficient reputation)
Try to add the #EnableWebMvc annotation to your configuration:
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
...
}

Spring controller return string as pure json

I am returning a string object from spring controller like
#RequestMapping(value = "/persons.html", method = RequestMethod.GET)
public #ResponseBody String listPersonHtml(Model model) {
return "{\"abc\":\"test\"}";
}
I am getting response on ui like "{\"abc\":\"test\"}",i want this response as
{"abc":"test"}
i.e pure json object.
what type of configuration I need?
On UI side,if I set Accept */* then I face this issue,if I set Accept text/html or Accept text/plain then no issue is there,but I can't change accept header.
I found the way.Its all about spring message-converters.I added MappingJackson2HttpMessageConverter in this list and this converter tries to convert string to json and produces this result.
Just add org.springframework.http.converter.StringHttpMessageConverter before MappingJackson2HttpMessageConverter so that StringHttpMessageConverter can come into action and string can be returned as it is.
Old question, but I just had to solve the same issue and most of the answers I found resulted misleading, so here's mine:
It all starts with the Controller, and Spring trying to answer a mapped request in the format that the invoking client is expecting. The client can inform this using different HTTP features, and there is where the different HttpMessageConverter implementations are involved. Spring pick's the format to answer based on different strategies, applied by the ContentNegotiationManager.
By prioritizing StringHttpMessageConverter over MappingJackson2XmlHttpMessageConverter you are only telling Spring to answer in "text/plain" format over "application/json", and it will work until a client specifies that is expecting a json response (this is mostly done by setting the Accept header in the request, although there are other ways to do it). The important thing is that if a client sets that header to "application/json", Spring will use MappingJackson2XmlHttpMessageConverter that will translate the Java String to a Json String, ending up with something like "{\"abc\":\"test\"}" instead of {"abc":"test"}
So, the real issue that every developer faces in this case is that MappingJackson2XmlHttpMessageConverter translates a Java String to a Json String, and in some cases, you might not need that, because the string contains valid json that needs to be returned without modifications. There are some configuration classes for this MessageConverter but I did'n went that road, because I need to return Strings like "raw" Json only in some specific endpoints (performance is the key driver). Here's an expample that resumes my "approach":
#RestController
#RequestMapping(value = "test", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class TestController {
#RequestMapping(method = RequestMethod.GET, value = "endpoint")
public JsonObject getSomeJson() {
return new JsonObject("{\"abc\":\"test\"}");
}
private static class JsonObject {
private String rawJsonValue;
JsonObject(String rawJsonValue) {
this.rawJsonValue = rawJsonValue;
}
#JsonValue #JsonRawValue
public String getRawJsonValue() {
return rawJsonValue;
}
}
}
#JsonValue and #JsonRawValue are Jackson annotations that tell MappingJackson2XmlHttpMessageConverter to treat the getRawJsonValue method result as the Json representation of JsonObject, without making any modification. The response of the endpoint will be {"abc":"test"}

HttpServletRequest - Get Literal Path

I have a method marked with Spring's #RequestMapping that includes an HttpServletRequest method parameter.
If I print out the results of a call to "request.getServletPath()" when the path is, say, "/things/{thingId}", I will get "/things/2489sdfjk43298f," where the {thingId} path parameter has been replaced with the actual value.
I want to print out the literal request path "/things/{thingId}"; I.e. with the curly-braced, un-replaced path parameter "{thingId}."
Is this possible in any way?
Edit: After looking at Sotirios's second comment below, I realize I may be looking at the problem backward. Here's what I'm actually trying to do...
I am trying to making a single endpoint under "/**" that gets the path from the HttpServletRequest, which I use to look up a value in an enum. This enum has several fields, one of which is obviously the aforementioned path, but another is the path of a target JSP file. I then put this path into a ModelAndView object and return it to display the page.
This was going just fine until I hit the first endpoint with a path parameter, because I obviously can't place the value "/things/2489sdfjk43298f" into the enum, because that will only match for that one specific thing with that one specific ID.
So perhaps the actual question would be: How would I do that look-up when parts of the path will change due to path parameters? Is there some sort of wildcard-containing String format I can use?
I guess this is turning into more of a enum-lookup/String-matching question. My bad.
Edit 2: Shortened example of the enum thing I'm talking about:
public enum JspEndpointType {
HOME("/home", "jsp/home");
private static final Map<String, String> pathMap;
private String requestPath;
private String jspPath;
static {
pathMap = new HashMap<>();
for (JspEndpointType jspEndpointType : JspEndpointType.values()) {
pathMap.put(jspEndpointType.getRequestPath(), jspEndpointType.getJspPath());
}
}
private JspEndpointValue(String requestPath, String jspPath) {
this.requestPath = requestPath;
this.jspPath = jspPath;
}
public String getRequestPath() {
return requestPath;
}
public String getJspPath() {
return jspPath;
}
public static String getByRequestPath(String requestPath) {
return pathMap.get(requestPath);
}
}
Shortened example of my endpoint:
#RequestMapping(value = "/**", method = RequestMethod.GET)
public ModelAndView showPage(HttpServletRequest request) {
return new ModelAndView(JspEndpointType.getByRequestPath(request.getServletPath()));
}
So things essentially boil down to trying to add to the enum a value like this:
THINGS("/things/{thingId}", "jsp/things/whatever")
..and then being able to pass in the path "/things/2489sdfjk43298f" and get back "/jsp/things/whatever."
Edit 3: I found this StackoverFlow question which directed me to Spring's UriComponentsBuilder, specifically the "fromPath" method. However, that seems to be the reverse of what I'm trying to do...
You may look for the #RequestMapping annotation on your own, using reflection.

Jackson JSON mapping of Strings

I used POJO's as #RequestBody parameters and they worked fine. But now I try something like this public void func(#RequestBody String s) and cant pass the value to String s. When I used POJO's I writes {"attribute" : "value"} in request and the question is what I should write in POSTMAN to pass the value?
If you want to send only string value then it is better idea to use #RequestParam and in postman client you can send key value parameter.
public String method(#RequestParam("key") String value){
}

Resources