Spring controller invoked 2 times - java

I have my project with SpringBoot 1.5.1 gradle.
I need to response with pdf file with "OK" or some other statuses.
So problem is when I request with "Postman" code invokes 2 times.
when I request with "curl" code invokes 1 time.
Obviously I want to invoke it 1 time.
I have an application class with:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And my controller with:
#RequestMapping(value = "/report/{reportTemplate:.+}", method = POST)
#ResponseBody
public ResponseEntity createReport(HttpEntity<List<ParametersEntity>> httpEntity,
#PathVariable String reportTemplate) throws IOException {
byte[] data = ...;// my data
return ResponseEntity
.ok()
.contentLength(data.length)
.contentType(MediaType.APPLICATION_PDF)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.pdf")
.contentType(MediaType.parseMediaType(MediaType.APPLICATION_PDF_VALUE))
.body(data);
}
createReport is in #RestController class.
Also I have application.properties file in my src/main/resources/
server.port: 10500
management.port: 10501
management.address: 127.0.0.1

Your code looks OK. Nothing wrong with it.
If you are in DEBUG mode when invoking the request from the POSTMAN it may confuse it and send the request for a second time. I gues it depends on some configurations. But while you have no problems with CURL you must search the problem in POSTMAN probably not your code.

Related

Spring MVC Restful Multipart Form Not Working

I've created a Restful service with Spring MVC as shown below. I called it using Postman. I placed a breakpoint on 'return "hello World"'. There's no hit on the breakpoint with the error message "Required request part 'file' is not present".
However, if I comment out the '#RequestParam("file")' annotation, the breakpoint is hit with the parameter "file" being null.
What could have gone wrong? Very puzzled.
#RestController
#RequestMapping("/dp")
public class DpWebService implements IDpWebService {
#Override
#Bean
public MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
#Override
#Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
#Override
#RequestMapping(path = "/send", method = RequestMethod.POST, consumes = "multipart/form-data")
public String sendManifest(#RequestParam("file") MultipartFile file) {
return "Hello World";
}
}
Postman
Postman Header
Check your POSTMAN request Configuration. I think you have not changed the input type to File from Text. Uploading images, check the images. Hover the mouse over that area in Postman and select File from the drop-down menu.
Having Beans defined in your RestController is not a good design. Please separate out a Configuration class with #Configuration annotation and define your beans. The reasons being: Single Responsibility Principle - each class should only do about one thing.
https://java-design-patterns.com/principles/#single-responsibility-principle
#RequestParam might not be working for you because of the nature of the data that is contained in the file that you are sending through the request. RequestParam is likely to be used with name-value form fields. For complex data like json/xml it is advisable to use #RequestPart instead.
Instead of the #RequestParam annotation use the #RequestPart annotation.
Annotation that can be used to associate the part of a
"multipart/form-data" request with a method argument.
Try using it like :
#RestController
#RequestMapping("/dp")
public class DpWebService implements IDpWebService {
#Override
#Bean
public MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
#Override
#Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
#Override
#RequestMapping(path = "/send", method = RequestMethod.POST, consumes = "multipart/form-data")
public String sendManifest(#RequestPart("file") MultipartFile file) {
return "Hello World";
}
}
Also make sure that the request from the postman is getting triggered correctly :
Remove any un wanted request params from postman.
Make sure that under 'Body' tab the form-data is selected. Also make
sure that when selected the file in the key the name is provided as
'file' and type is also selected as file instead of text.
This is my working example.
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath().path("/downloadFile/")
.path(fileName).toUriString();
return new UploadFileResponse(fileName, fileDownloadUri, file.getContentType(), file.getSize());
}
application.properties
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
## File Storage Properties
# Please change this to the path where you want the uploaded files to be stored.
file.upload-dir=C://Users//abc//Documents//

SpringBoot + Postman #RequestMapping value = "getImage/{imageName:.+}"

I an creating an endpoint with spring boot...i can upload image to folder and save it via postman everythink works good.
i have a problem with get method when i am adding the value #RequestMapping value = "getImage/{imageName:.+}" in postman i add http://localhost:8080/api/images/getImage/{burger+png}
is that corect ???
#RequestMapping(value = "api/images")
public class ImageController {
#Autowired
public ImageService imageService;
#PostMapping(value ="upload")
public ResponseEntity uploadImage(#RequestParam MultipartFile file){
return this.imageService.uploadToLocalFileSystem(file);
}
#GetMapping(
value = "getImage/{imageName:.+}",
produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_GIF_VALUE,MediaType.IMAGE_PNG_VALUE}
)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable(name = "imageName") String fileName) throws IOException {
return this.imageService.getImageWithMediaType(fileName);
}
}
what should be the correct request url ???
It seems like it's reaching the backend fine, but failing to find path. Usually API endpoints end with parameters with a slug or query param. You can try either of the following to see if it works:
http://localhost:8080/api/images/getImage/burger.png
http://localhost:8080/api/images/getImage?imageName=burger.png
Keep in mind, you want to make sure that file exists at the path it's mentioning at the very top of the trace in the JSON response. This may depend on how you uploaded the file and with what name.

Documenting PUT REST call without body and with path and query parameters

There's REST API call designed via HTTP PUT that has only path and query parameters and does not need a body:
PUT /posts/{id}/read?currentUser={loginId}
Trying to document it using Spring REST Docs 2.0.0.RELEASE I noticed that http-request.adoc is as below:
[source,http,options="nowrap"]
----
PUT /posts/42?currentUser=johndoe HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
currentUser=johndoe
----
I am confused, why currentUser=johndoe is rendered in body (like form parameter)? Is it a bug? Complete example of application below:
#RestController
#RequestMapping("/posts")
#SpringBootApplication
public class DemoApplication {
#PutMapping("{id}/read")
public void markPostRead(#PathVariable("id") String id, #RequestParam("currentUser") String login) {
System.out.println("User " + login + " has read post " + id);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#RunWith(SpringRunner.class)
#WebMvcTest
public class DemoApplicationTests {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void contextLoads() throws Exception {
mockMvc.perform(
RestDocumentationRequestBuilders.put("/posts/{id}/read?currentUser=johndoe", 42))
.andDo(document("mark-as-read", pathParameters(
parameterWithName("id").description("ID of the Post")
)))
.andDo(document("mark-as-read", requestParameters(
parameterWithName("currentUser").description("Login ID of user")
)));
}
}
If you study RFC 2616 with special attention on the PUT section you will read…
The PUT method requests that the enclosed entity be stored under the supplied Request-URI.
So the question is, although the spec is not 100 % clear in this part: Is it allowed to send a PUT request with no request body at all?
This is one of the questions on which you get 11 answers if you ask 10 developers but just to be fine with all imponderables, Spring REST Docs puts your query parameter in the request body just to be prepared for all those little more strict web servers out there. ;-)
Also, according to your endpoint "#PutMapping("{id}/read")", I noticed that in your REST documentation test the "/read" part of the path is missing.

response.sendRedirect shows unwanted HttpStatus 302 instead of 307

I have a small test, which should return a HttpStatus with Temporary Redirect with HttpStatus Code 307.
But it always returns a 302.
#RestController
#RequestMapping(value = "/")
public class Controller {
#ResponseStatus(HttpStatus.TEMPORARY_REDIRECT )
#RequestMapping(value= "test", method = RequestMethod.GET)
public void resolveUrl(HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
response.sendRedirect("https://www.google.com");
}
}
When I look into the documentation of response.sendRedirect() I can read this:
Sends a temporary redirect response to the client using the specified
* redirect location URL.
and the documentation of temporary redirect is a 307:
10.3.8 307 Temporary Redirect
The requested resource resides temporarily under a different URI.
Since the redirection MAY be altered on occasion, the client SHOULD
continue to use the Request-URI for future requests. This response is
only cacheable if indicated by a Cache-Control or Expires header
field.
(I know, that I don't need the #ResponseStatus(HttpStatus.TEMPORARY_REDIRECT) or the response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); but I want to show that it will not work with this things too!)
But my test shows that it was a 302 and not a 307
java.lang.AssertionError: Status expected:<307> but was:<302>
Can somebody explain this?
My small test for this:
#RunWith(SpringRunner.class)
#WebMvcTest(Controller.class)
public class ControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void test() throws Exception {
MockHttpServletRequestBuilder requestBuilder = get("/test");
mvc.perform(requestBuilder).andExpect(status().isTemporaryRedirect())
.andExpect(redirectedUrl("https://www.google.com"));
}
}
Complete code can be found at github
Instead using sendRediect , set Location header in response object.
response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
response.setHeader("Location","https://we.google.com");

Test file upload Spring MVC

I would like to test file uploading by REST API. I found it quite confusing to send file as RequestParam instead of RequestBody.
Controller method:
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public
#ResponseBody
ResponseEntity<String> uploadFile(
#RequestParam(name = "file") MultipartFile multipartFile,
#RequestParam(name = "path") String path) {
logger.debug("File upload REST requested");
return new ResponseEntity<>(fileService.uploadFile(
multipartFile, path),
HttpStatus.OK);
}
1.Now I would like to test it and I've used mocks. While debugging it, I see that mock service working but method exactly with this arguments is not invoked, so the test if failed. How could I handle this?
#Test
public void testUploadFile() throws Exception {
String mockName = "mock";
MockMultipartFile mockMultipartFile = new MockMultipartFile(mockName, mockName.getBytes());
when(mockFileService.uploadFile(mockMultipartFile, rootDir)).thenReturn("success");
mockMvc.perform(MockMvcRequestBuilders.fileUpload("/files/upload")
.file("file", mockMultipartFile.getBytes())
.param("path", rootDir))
.andExpect(status().isOk())
.andExpect(content().string("success"));
verify(mockFileService, times(1)).updateFile(mockMultipartFile, rootDir);
verifyNoMoreInteractions(mockFileService);
}
2.How could I test this with Postman? I see that I can send file in Body, but how could I send it as param?
EDIT:
I've changed the method, but the problem is not there:
Argument(s) are different! Wanted:
mockFileService.uploadFile(
org.springframework.mock.web.MockMultipartFile#61bd0845,
"/"
);
Looks like method are using 2 instances of MockMultipartFile. And the second question is still open, how could this method be tested from Postman?
Yes, the test case fails, because
the problem is in your test case at the end, you are verifying the call to updateFile() which is incorrect as in your controller you are using uploadFile(), you MUST use the same method to verify.
So, you need to change the verify line as below:
verify(mockFileService, times(1)). uploadFile(mockMultipartFile, rootDir);
In other words, Mockito verify validates the number of times a method is invoked as you are trying to verify the call on a different method (not being used in controller), it is failing.

Categories

Resources