How to map request contains File and data in Rest service using #RestController with HttpEntity<class> as input parameter - spring

Please see below code where i want to send request having file and other json data which will be in a single java class:
My Class is(used lombok):
#Data
public class CustomFileUploadSearch {
private Long selectedId;
private MultipartFile file;
}
#RequestMapping(method = RequestMethod.POST, path = "/rest/GIER/testFileData",
consumes = {"multipart/form-data"}, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity bulkActivateInactivate(HttpEntity<CustomFileUploadSearch> entity, HttpServletRequest request) {
CustomFileUploadSearch cfuSearch = entity.getBody();
}
I am not able to reach into this method.
Please help me so that it has already taken 1 day.
Thanks in advance.
#RestController
public class ChassisInventoryRest {
#SuppressWarnings("rawtypes")
#RequestMapping(method = RequestMethod.POST, path = "/rest/GIER/testFileData",
consumes = {"multipart/form-data"}, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity bulkActivateInactivate(HttpEntity<CustomFileUploadSearch> entity, HttpServletRequest request) {
CustomFileUploadSearch cfuSearch = entity.getBody();
System.out.println("cfuSearch.getSelectedId():"+cfuSearch.getSelectedId());
System.out.println("cfuSearch.getFile():"+cfuSearch.getFile());
return new ResponseEntity<Map<String, Object>>( new HashMap<String, Object>(), HttpStatus.OK);
}
}

public class CustomFileUploadSearch {
private Long selectedId;
private MultipartFile file;
public Long getSelectedId() {
return selectedId;
}
public void setSelectedId(Long selectedId) {
this.selectedId = selectedId;
}
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity bulkActivateInactivate(CustomFileUploadSearch entity, HttpServletRequest request) {
System.out.println(entity.getSelectedId());
System.out.println(entity.getFile());
}

You can explicitly get MultipartFile as request input parameter in RestController.
#RestController
public class ChassisInventoryRest {
#PostMapping(path = "/rest/GIER/testFileData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity bulkActivateInactivate(#RequestParam MultipartFile file, #RequestParam Long selectedId) {
//parameterized constructor
CustomFileUploadSearch cfuSearch = new CustomFileUploadSearch(selectedId, file);
return new ResponseEntity<Map<String, Object>>( new HashMap<String, Object>(), HttpStatus.OK);
}
}

Related

Spring Web: How to transfer many multipart file and JSON to #RestController

I have challenge to create spring controller which consumes array of MultipartFile and JSON.
I've already done controller for JSON and one Multipart file:
Controller:
#PostMapping("/upload")
#ApiOperation("Загрузить новый файл")
#ResponseBody
public Map uploadFile(#RequestPart("file") MultipartFile file,
#RequestPart(required = false) JsonObject json) throws IOException {
UUID fileId = fileService.save(file);
UUID jsond = jsonService.save(json);
return ImmutableMap.of("fileId", fileId, "jsond", jsond );
}
Config file:
#Component
public class JsonConventerConfig extends AbstractJackson2HttpMessageConverter {
protected JsonConventerConfig(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
#Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}
#Override
protected boolean canWrite(MediaType mediaType) {
return false;
}
}
But I have no idea how to adapt my controller for array with MultipartFile.
Thank you!
The problem is solved by simply adding an array to #RequestPart.
#PostMapping(value = "/file")
#ApiOperation("Загрузить новый файл")
#ResponseBody
public Set<UUID> uploadFile(#RequestPart MultipartFile[] files,
#RequestPart(required = false) Document document) throws IOException
As it turned out swagger does not support multipart requests, so I were getting exception during testing.

Get return value in custom annotation spring aop

I have write a simple custom annotation to set HttpHeaders to ResponseEntity because of duplicating the code every where .
Annotation Interface and Class.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonHeader {}
#Component
#Aspect
public class JsonHeaderAspect {
private final Log logger = LogFactory.getLog(getClass());
#Around(value = "#annotation(JsonHeader)")
public Object aroundServiceResponse(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
return proceedingJoinPoint.proceed(new Object[] {headers});
}
}
RestController Class
#RestController
#RequestMapping(path = "/login")
public class LoginRestController {
private final Log logger = LogFactory.getLog(getClass());
#Autowired
LoginServiceImpl loginService;
#JsonHeader
#RequestMapping(value = "/user",consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseBean> postCheckUser(#RequestBody LoginBean loginBean) {
ResponseBean responseBean = loginService.checkUser(loginBean);
return new ResponseEntity<ResponseBean>(responseBean,headers, HttpStatus.OK);
}
}
Now I want to get the return HttpHeaders value annotaion to rest controller class.
Is any one can describe why it happen and how to fix this issue it will be great helpful. Thanks in advance
You can do this simply by modifying your advice like this. You don't need to do anything in the controller.
#Around(value = "#annotation(requestMapping)")
public Object aroundServiceResponse(ProceedingJoinPoint proceedingJoinPoint,RequestMapping requestMapping) throws Throwable {
String[] consumes = requestMapping.consumes();
consumes[consumes.length] = MediaType.APPLICATION_JSON_VALUE;
String[] produces = requestMapping.produces();
produces[produces.length] = MediaType.APPLICATION_JSON_VALUE;
return proceedingJoinPoint.proceed();
}

In the Spring3,How to call a another server's controller in my controller

I have 3 servers,serverA,serverB,serverC,Now in the serverC,some request from serverB is by processed,and then,I don't know what is the result(response),if it's resultA,I want give the resultA to the serverA as a request,else give the serverB.
so what I can do something in the serverC's controller,or there is something wrong in the desgin.
Please tell me what I should to do,Thanks.
This is my code.
serverA
#RestController
public class ControllerA {
#RequestMapping(value = "/methodA", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<String> methodA(#RequestBody String something) {
// some process
return null;
}
serverB
#RestController
public class ControllerB {
#RequestMapping(value = "/methodB", consumes =MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> methodB(#RequestBody String something) {
// some process
return null;
}
serverC
#RestController
public class ControllerC {
public ResponseEntity<String> methodC(#RequestBody String someReq) {
if (checkPam(someReq)) {
**// I want to call the ControllerA in serverA.**
}else {
**// I want to call the ControllerB in serverB.**
}
return null;
}
You can simply Use RestTemplate:
#RestController
public class ControllerC {
public ResponseEntity<String> methodC(#RequestBody String someReq) {
RestTemplate restTemplate = new RestTemplate();
if (checkPam(someReq)) {
String fooResourceUrl
= "http://path-to-server-a/path-to-service-a";
ResponseEntity<String> response
= restTemplate.getForEntity(fooResourceUrl , String.class);
}else {
String fooResourceUrl
= "http://path-to-server-b/path-to-service-b";
ResponseEntity<String> response
= restTemplate.getForEntity(fooResourceUrl , String.class);
}
return null;
}
As you can see, I instantiate RestTemplate object by new operator, you can also declare RestTemplate bean in your context and then autowire it in your controller class.

Rest template giving null body and status 302

I am trying to consume a rest call in my mvc controller, however every time I do it returns a null body with http status as 302.Also I am using spring boot with spring security to get https.
I've followed code samples from here: http://websystique.com/springmvc/spring-mvc-4-restful-web-services-crud-example-resttemplate/
and Get list of JSON objects with Spring RestTemplate however none of these work
Can someone please point me in the right direction
Thank you,
REST
#RequestMapping(value = "/api/*")
#RestController
public class PostApiController {
static final Logger logger = LogManager.getLogger(PostApiController.class.getName());
private final PostService postService;
#Inject
public PostApiController(final PostService postService) {
this.postService = postService;
}
//-------------------Retrieve All Posts--------------------------------------------------------
#RequestMapping(value = "post", method = RequestMethod.GET)
public ResponseEntity<List<Post>> getAllPosts() {
List<Post> posts = postService.findAllPosts();
if(posts.isEmpty()){
return new ResponseEntity<List<Post>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<Post>>(posts, HttpStatus.OK);
}
}
Controller
#Controller
public class PostController {
static final Logger logger = LogManager.getLogger(PostController.class.getName());
public static final String REST_SERVICE_URI = "http://localhost:8080/api"; //"http://localhost:8080/api";
private final PostService postService;
#Inject
public PostController(final PostService postService) {
this.postService = postService;
}
#SuppressWarnings("unchecked")
#RequestMapping(value = "/getAll")
// public String create(#Valid Post post, BindingResult bindingResult, Model
// model) {
public ModelAndView getAll() {
// if (bindingResult.hasErrors()) {
// return "mvchome";
// }
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Post>> responseEntity = restTemplate.exchange(REST_SERVICE_URI+"/post",HttpMethod.GET, null, new ParameterizedTypeReference<List<Post>>() {});
// ResponseEntity<Post[]> responseEntity = restTemplate.getForEntity(REST_SERVICE_URI+"/post", Post[].class);
List<Post> postsMap = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();
// List<LinkedHashMap<String, Object>> postsMap = restTemplate.getForObject(REST_SERVICE_URI+"/post", List.class);
// String s= REST_SERVICE_URI+"/post";
// logger.info(s);
if(postsMap!=null){
for(Post map : postsMap){
logger.info("User : id="+map.getUid());
}
}else{
logger.info("No user exist----------");
}
//List<Post> postList = postService.findAllPosts();
ModelAndView mav = new ModelAndView("mvchome");
mav.addObject("postsList", postsMap);
Post newpost = new Post();
mav.addObject("post", newpost);
return mav;
}
}
***** to fix my issue I modified my code to just do a redirect on select url paths instead of "/*"
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat =
new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
//used to be just collection.addPattern("/*"); now I changed it to specify which path I want it to redirect
collection.addPattern("/mvchome/*");
collection.addPattern("/home/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(createHttpConnector());
return tomcat;
}
The http status 302 is usually caused by wrong url setting.
First, make sure that public ResponseEntity<List<Post>> getAllPosts() {} method is called (just print List<Post> result inside it).
If it's called properly and you can get the return value inside public ModelAndView getAll() {}.
The problem should be the directing setting of the public ModelAndView getAll() {} method.
Check if you make something wrong in your web.xml or spring configuration. Pay attention to the configuration which redirects to views and the url mapping of your dispatcher servlet.
If public ResponseEntity<List<Post>> getAllPosts() {} is called but you can't get the return value, then it should be the issues of directing setting of the public ResponseEntity<List<Post>> getAllPosts() {} method.
Check your spring configuration and web.xml for that. The possible cause usually will be the misuse of wildcard in the configuration and web.xml, or just unnoticed wrong mapping.

How to access plain json body in Spring rest controller?

Having the following code:
#RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String greetingJson(#RequestBody String json) {
System.out.println("json = " + json); // TODO json is null... how to retrieve plain json body?
return "Hello World!";
}
The String json argument is always null despite json being sent in the body.
Note that I don't want automatic type conversion, I just want the plain json result.
This for example works:
#RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String greetingJson(#RequestBody User user) {
return String.format("Hello %s!", user);
}
Probably I can use the use the ServletRequest or InputStream as argument to retrieve the actual body, but I wonder if there is an easier way?
Best way I found until now is:
#RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String greetingJson(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
// json contains the plain json string
Let me know if there are other alternatives.
You can just use
#RequestBody String pBody
Only HttpServletRequest worked for me. HttpEntity gave null string.
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
#RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String greetingJson(HttpServletRequest request) throws IOException {
final String json = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
System.out.println("json = " + json);
return "Hello World!";
}
simplest way that works for me is
#RequestMapping(value = "/greeting", method = POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public String greetingJson(String raw) {
System.out.println("json = " + raw);
return "OK";
}
If you have dozens of Methods that need to get HTTP body as JSON and convert it to custom data type, it is a better way to implement the support on the framework
public static class Data {
private String foo;
private String bar;
}
//convert http body to Data object.
//you can also use String parameter type to get the raw json text.
#RequestMapping(value = "/greeting")
#ResponseBody
public String greetingJson(#JsonBody Data data) {
System.out.println(data);
return "OK";
}
notice that we using user defined annotation #JsonBody.
// define custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface JsonBody {
String encoding() default "utf-8";
}
//annotation processor for JsonBody
#Slf4j
public class JsonBodyArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(JsonBody.class) != null;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
JsonBody annotation = parameter.getParameterAnnotation(JsonBody.class);
assert annotation != null;
ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
if (servletRequest == null) {
throw new Exception("can not get ServletRequest from NativeWebRequest");
}
String copy = StreamUtils.copyToString(servletRequest.getInputStream(), Charset.forName(annotation.encoding()));
return new Gson().fromJson(copy, parameter.getGenericParameterType());
}
}
// register the annotation processor
#Component
public class WebConfig implements WebMvcConfigurer {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new JsonBodyArgumentResolver());
}
}
As of 4.1 you can now use RequestEntity<String> requestEntity and access the body by requestEntity.getBody()
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/RequestEntity.html

Resources