Spring MVC javax.ws.rs.client - how to pass to REST controller both : #RequestParam and #RequestBody? - spring-mvc

I have Spring REST controller
#PostMapping("/complexTrace")
fun complexTrace(#RequestParam projectId: UUID, #RequestBody filter: RegulationTraceFilter): List<RegulationItemTraceResult> {
I need to call it with #RequestParam and #RequestBody but I don't know how. I know how to pass only a body:
val testResponse = this.resteasyClient.target("http://localhost:$port$relativePath")
.request().header("Authorization", "Bearer $accessToken").post(Entity.json(requestBody))
But how to pass #RequestParam's value?

The #RequestParam means query parameter. For your case , the URL looks like /complexTrace?projectId=bedb3c38-4420-42ec-8151-549f285e9da8.
You can use queryParam() to configure it :
val testResponse = this.resteasyClient.target("http://localhost:$port$relativePath")
.queryParam("projectId", "bedb3c38-4420-42ec-8151-549f285e9da8")
.request().header("Authorization", "Bearer $accessToken").post(Entity.json(requestBody))

Related

How to resolve MethodArgumentConversionNotSupportedException with MockMvc?

I'm writing a unit test for a controller method that accepts a MultipartFile
and a custom object MessageAttachment. So far I can see that the MultipartFile is the correct format for the request but the MessageAttachment is not.
The parsing of the messageAttachment throws a server side 500 error with MethodArgumentConversionNotSupportedException.
It seem to be an issue with converting the MessageAttachment to a MockMultipartFile in the test. This is similar to the example shown here - https://stackoverflow.com/a/21805186
Question:
How can you resolve a MethodArgumentConversionNotSupportedException with MockMvc?
Controller method under test
#RequestMapping(value = "/", method = RequestMethod.POST, consumes = "multipart/form-data", produces = "application/json")
public ResponseEntity<MessageAttachment> handleFileUpload(#RequestParam(value = "file", required = true) MultipartFile file, #RequestParam(value = "messageAttachment") MessageAttachment messageAttachment) {
//do stuff with the file and attachment passed in..
MessageAttachment attachment = new MessageAttachment();
return ResponseEntity.accepted().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getOriginalFilename() + "\"").body(attachment);
}
MockMvc Test
#Test
public void shouldSaveUploadedFile() throws Exception {
// Given
ObjectMapper mapper = new ObjectMapper();
MessageAttachment messageAttachment = new MessageAttachment();
messageAttachment.setTimestamp(new Date());
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt", "text/plain",
"Spring Framework".getBytes());
//Mapping the msgAttachment to a MockMultipartFile HERE
MockMultipartFile msgAttachment = new MockMultipartFile("messageAttachment", "","application/json",
mapper.writeValueAsString(messageAttachment).getBytes());
// When
this.mockMvc.perform(MockMvcRequestBuilders.multipart("/media/")
.file(multipartFile)
.file(msgAttachment)).andDo(MockMvcResultHandlers.print());
}
Console output of MockMvcResultHandlers.print()
MockHttpServletRequest:
HTTP Method = POST
Request URI = /media/
Parameters = {}
Headers = {Content-Type=[multipart/form-data]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.fizz.buzz.fizzapi.controller.MediaUploadController
Method = public org.springframework.http.ResponseEntity<com.fizz.buzz.fizzapi.model.MessageAttachment> com.fizz.buzz.fizzapi.controller.MediaUploadController.handleFileUpload(org.springframework.web.multipart.Mu
ltipartFile,com.fizz.buzz.fizzapi.model.MessageAttachment,javax.servlet.http.HttpServletRequest)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
You'll want to use #RequestPart instead of #RequestParam for the part of the request that is application/json. The javadoc for #RequestPart states
Supported method argument types include MultipartFile in conjunction
with Spring's MultipartResolver abstraction,
javax.servlet.http.Part in conjunction with Servlet 3.0 multipart
requests, or otherwise for any other method argument, the content of
the part is passed through an HttpMessageConverter taking into
consideration the 'Content-Type' header of the request part. This is
analogous to what #RequestBody does to resolve an argument based on
the content of a non-multipart regular request.
Note that #RequestParam annotation can also be used to associate the
part of a "multipart/form-data" request with a method argument
supporting the same method argument types. The main difference is that
when the method argument is not a String, #RequestParam relies on type
conversion via a registered Converter or PropertyEditor while
#RequestPart relies on HttpMessageConverters taking into consideration
the 'Content-Type' header of the request part. #RequestParam is likely
to be used with name-value form fields while #RequestPart is likely to
be used with parts containing more complex content (e.g. JSON, XML).
Presumably, you haven't registered a Converter, nor a PropertyEditor, to parse the content of that part, whereas an HttpMessageConverter for JSON is automatically registered (depending on your Spring MVC/Boot version) if you have Jackson on the classpath.

Send object as parameter in client.PutAsync

I am facing a problem in using PutAsync. PutAsync update an object. Below is my code. (Mongodb database)
Controller Code:
stringData = JsonConvert.SerializeObject(businessUnit); //businessUnit is updated object
var contentData = new StringContent(stringData, System.Text.Encoding.UTF8, "application/json");
response = client.PutAsync(baseAddress + "/api/BusinessUnit/" + businessUnit.Id, contentData).Result;
API Controller Code :
[HttpPut("{id}")]
public async Task<string> Put(string id, BusinessUnit businessUnit)
{
if (string.IsNullOrEmpty(id)) return "Invalid id !!!";
return await _businessUnitRepository.Update(id, businessUnit);
}
Given code works good but my problem is in API controller businessUnit parameter's all fields become null instead of id.
My Confusion is, if businessUnit parameter's all fields are null then why its primary key "id" is not null ??
I want to get all fields as parameter in businessUnit object from controller to api controller. How can I do it?
Thanks in advance.
Add the [FromBody] attribute to the 'businessUnit' parameter:
public async Task<string> Put(string id, [FromBody] BusinessUnit businessUnit)
This is called an Model Binding and allows to map data from HTTP requests to action method parameters:
[FromBody]: Use the configured formatters to bind data from the request body. The formatter is selected based on content type of the request.

How to access Request Payload in Spring MVC?

How can I access the Request Payload in Spring MVC ?
PS: I know the question has been asked before, here : Access "Request payload" in Spring MVC Controller?
However, I didn't get the way it should be done.
Thanks!
Using #RequestBody.
For example:
#RequestMapping(value = "/add")
public String add(#RequestBody String json, HttpServletRequest request) {
return json;
}

modify HTTP request URI and HTTP request method with a CXF interceptor

I want to modify HTTP request URI and HTTP request method using a CXF interceptor in a HTTP client.
I have developed something like this:
public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
public MyInterceptor() {
super(Phase.PRE_PROTOCOL);
}
public void handleMessage(Message message) {
// this returns me correct path and method
// String path = (String) message.getExchange().getOutMessage().get(Message.REQUEST_URI);
// String method = (String) message.getExchange().getOutMessage().get(Message.HTTP_REQUEST_METHOD);
// this does not work as expected
String path = (String) message.get(Message.REQUEST_URI);
String method = (String) message.get(Message.HTTP_REQUEST_METHOD);
// do things here
}
}
Why do need I to use exchange/OutMessage to obtain data about current message and I can not use message directly?
How can I edit both values? I tried using message.put(<key>, <value>) and the same with exchange/OutMessage, but nothing is modified.
Coming to the path, you'd always get that value as null, I believe.
You can try following code, to get the actual value of your uri:
String requestURI = (String) message.get(Message.class.getName() + ".REQUEST_URI");

How to set content type dynamically in a Spring MVC controller (depending on presence of request param)?

I have a REST API that until now always returned JSONP (JSON data wrapped in whatever function call client wanted):
static final String JAVASCRIPT = "application/javascript;charset=UTF-8";
#RequestMapping(value = "/matches", produces = JAVASCRIPT)
#ResponseBody
public String matches(#RequestParam String callback) {
String json = jsonService.getCachedJson("/matches");
return toJsonp(callback, json);
}
Now, things have changed so that I need to return either JSON or JSONP: if client provides a callback function name, we return JSONP and otherwise pure JSON.
With regards to content type, I'd like to be as correct as possible and use application/json for JSON and application/javascript for JSONP.
So, something like this:
#RequestMapping(value = "/matches")
#ResponseBody
public String matches(#RequestParam(required = false) String callback) {
String json = jsonService.getCachedJson("/matches");
// TODO: if callback == null, set content type to "application/json",
// otherwise to "application/javascript"
return jsonOrJsonp(callback, json);
}
String jsonOrJsonp(String callback, String json) {
return Strings.isNullOrEmpty(callback) ? json : toJsonP(callback, json);
}
Looks like I can no longer use produces attribute of #RequestMapping. What's the simplest way to set content type with Spring MVC in the scenario above?
I'd like to avoid defining HttpMessageConverters (or other Spring hassle) or changing the method return type, if at all possible! And obviously I wouldn't like duplicated method declarations where produces value is the only significant difference. What I'm looking for is minimal changes to the above code.
Latest Spring (3.2.3).
Have you tried just using two request handler methods?
#RequestMapping(value = "/matches", produces = JAVASCRIPT, params="callback")
#ResponseBody
public String Jsonp(#RequestParam String callback) {
return toJsonp(callback, jsonService.getCachedJson("/matches"));
}
#RequestMapping(value = "/matches", produces = JSON)
#ResponseBody
public String json() {
return toJson(jsonService.getCachedJson("/matches"));
}
The first method with the params parameter will only be mapped to requests where the callback param is present.

Resources