Hide HttpServletRequest request in Swagger OpenAPI 3 - java

I am currently using Spring Boot 3.0.2 with Swagger OpenAPI 3. But the SwaggerUI keeps marking a parameter in my controller as a required request parameter.
In my pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
In my RestController.java
#GetMapping("/endpoint")
public ResponseEntity<Object> Hello(HttpServletRequest request,
#RequestParam String paramOne){}
In my swagger UI, there are two required parameters: paramOne and request (which I don't want to be a part of). How can I hide or mark it as not a URL parameter?

Mark the HttpServletRequest parameter with #Parameter(hidden = true). Your code should look like this:
#GetMapping("/endpoint")
public ResponseEntity<Object> Hello(#Parameter(hidden = true) HttpServletRequest request,
#RequestParam String paramOne){}
This will hide the request parameter in Swagger UI and it will not be visible.

Related

Why SpringDoc OpenAPI doesn't understand MultipartFile payload?

Friends,
I am working on a Spring boot application which has a controller to help upload Multipart files.
#PostMapping("/files")
public ResponseEntity<?> uploadFiles(#RequestParam("file") MultipartFile[] file, String comment) throws IOException, ExecutionException, InterruptedException {
log.debug("Total files to store: {}", file.length);
log.debug("comment: {}", comment);
fileService.storeFile(Arrays.asList(file), comment);
return ResponseEntity.ok(environment.getProperty("file.upload.success"));
}
Problem: Somehow OpenDocAPI (swagger) doesn't understand this payload as file. It shows this field as "string" if I mention #RequestParam("file") MultipartFile file, or "string[ ]" if I use array of MultipartFiles.
My Spring boot parent:
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
The spring doc openapi dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
The swagger page:
The result page when I click on "Try it out"
The "Execute" Button doesn't work
Any idea what am I missing in the controller?
PS - I tried with mentioning #PostMapping(value = "/files", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}), still openapi would only treat it as string.

Spring Boot File Upload - part is null after upgrade to Spring Boot 2.4

I use multipart file upload with upload requests similar to the following curl variant:
curl --request POST 'http://localhost:8080/api/upload' --form 'csv=#"/path/file.csv"'
The form data looks the following when viewed in browser:
------WebKitFormBoundarysT6cYRLsXHQ8EWtZ
Content-Disposition: form-data; name="csv"; filename="file.csv"
Content-Type: text/csv
------WebKitFormBoundarysT6cYRLsXHQ8EWtZ--
My Java code looks like this:
#PostMapping(value = "/api/upload")
public ResponseEntity<String> upload(#RequestBody MultipartFile csv) {
System.out.println(new String(csv.getBytes(), StandardCharsets.UTF_8));
return ResponseEntity.ok().body(null);
}
It used to work fine with Spring Boot 2.0 but once I upgraded to Spring Boot 2.4/2.5, the csv parameter is always null so csv.getBytes() throws NullPointerException.
What I've tried so far:
Changing #RequestBody to #RequestPart("csv") or #RequestParam("csv") makes the upload request return HTTP 400 code with the message "Required request part 'csv' is not present"
Adding consumes = MediaType.MULTIPART_FORM_DATA_VALUE to #PostMapping makes no difference.
I've found out that by default Spring Boot uses StandardServletMultipartResolver because MultipartAutoConfiguration is enabled, it can be seen here - https://github.com/spring-projects/spring-boot/blob/v2.4.13/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfiguration.java
I've found a workaround - adding CommonsMultipartResolver as a default resolver instead of StandardServletMultipartResolver solves the issue. This can be done by adding a new dependency and a corresponding bean:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
However Spring discourages using CommonsMultipartResolver as can be seen at https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.spring-mvc.multipart-file-uploads
It is recommended to use the container’s built-in support for
multipart uploads rather than introducing an additional dependency
such as Apache Commons File Upload.
That's why I'd like to use the built-in support. Any ideas how to solve it?
Adding filter before authentication filter fixed issue for me. Works with StandardServletMultipartResolver
public class HttpRequestPartsInitFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/")) {
if (request.getParts().isEmpty()) {
log.info("Request.getParts() is empty for={}", request.getPathInfo());
} else {
log.info("Request.getParts() is NOT empty for={}", request.getPathInfo());
}
}
filterChain.doFilter(request, response);
}
}
I've registered filter in security #Configuration class that extends WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilterBefore(new OktaErrorFilter(LOGIN_URL, OKTA_ERROR_URL),
OAuth2AuthorizationRequestRedirectFilter.class)
.addFilterBefore(new HttpRequestPartsInitFilter(), OktaErrorFilter.class)
.... }
I din't find explanation why this made it work.
Similar fix mentioned here:
Spring POST multipart/form-data, request parts always empty

Spring MVC How to provide injectable for controller method

Spring MVC controller methods accespt different parameters that are injected before the method is invoked. Like HttpServletRequest, HttpServletResponse, java.security.Principal, etc.
#RequestMapping("/test")
public String test(HttpServletRequest req, Principal user){}
How can I declare something that can be injected in a controlelr method?
#RequestMapping("/test")
public String test(MyCustomInjectable myInjectable){}
More on the specific case:
I want to parse the HttpServletRequest in some servlet filter and construct an object that will be used in the controller method. More precisely I will parse a JWT token and access the claims.
There is an option to create custom HandlerMethodArgumentResolver.

Spring controller 400 bad request and application/x-www-form-urlencod v/s application/json

I have following Spring controller mapping:
#RequestMapping(value="/isSomethingHappening", method = RequestMethod.POST)
public #ResponseBody JsonResponse isSomethingHappening(HttpServletRequest httpRequest,#RequestParam("employeeId") String employeeId,
ModelMap model) throws IOException{
If I invoke this as below then I get 400 response.
var requestData = {"employeeId":XYZ.application.employeeId};
XYZ.network.fireAjaxRequestAsync("application/json", "forms/testing/isSomethingHappening", requestData, function(response, status, xhr){
But if I invoke this as below then I get success response.
var requestData = {"employeeId":XYZ.application.employeeId};
XYZ.network.fireAjaxRequestAsync("application/x-www-form-urlencoded", "forms/testing/isSomethingHappening", requestData, function(response, status, xhr){
I figured the fix but I am not able to understand why first one gave me error when my request data object var requestData = {"employeeId":XYZ.application.employeeId}; remained unchanged and I just changed the content type. To me application/json looks more appropriate content type because my request data is a JSON object.
Also, I have other instances where my controller mapping is as below:
#RequestMapping(value = "/getOnFlyResults", method = RequestMethod.POST)
public #ResponseBody JsonResponse getOnFlyResults(HttpServletRequest httpRequest,
#RequestBody testingRequestVO testingRequestVO, ModelMap modelMap) throws IOException{
And for invoking this, I send request as below:
var requestData = {"employeeId":XYZ.application.employeeId,
"fName":XYZ.application.fName,
"lName": XYZ.application.lName,
"telephoneNumber":telephoneNumber,
"testMode":XYZ.constant.onFly};
XYZ.network.fireAjaxRequestAsync("application/json", "forms/testing/startTest", JSON.stringify(requestData), function(response, status, xhr){
I don't understand that why I have to stringify the data using JSON.stringify(requestData), if I do not do this then I will get 400.
Once I stringify then it becomes a string then my content type should have been text/plain but it works with application/json
Please note that I know the code fixes, but I want to understand the concept. I have read this and it doesn't explain the concept in details and queries I have.
It is common to use Jackson library in your Spring application to process JSON.
If you are using Ant, try adding Jackson to your libraries.
You can download the Jackson library directly from Maven Central. Here is an example Maven dependency block (but get the latest version):
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Make sure you have the annotation-driven tag in your Spring configuration:
<annotation-driven />
With or without Jackson, it may also help to specify that the controller endpoint produces or consumes JSON;
#RequestMapping(value="/getjson",
produces=MediaType.APPLICATION_JSON_VALUE, consumes=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody MyObject getJSON(){
return new MyObject();
}

Spring MVC: How to set body parameter in request browser and how to get this body parameters in controller in Spring MVC?

I followed may links and found I need to use #Requestbody annotation and I need to set Content-Type=application/x-www-form-urlencoded in header under #RequestMapping annotation. But I did not find any example like how can I set these body parameters in browser and get in controller
#RequestMapping(value = "/login", headers="application/x-www-form-urlencoded" , method = RequestMethod.GET)
public void login(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServiceException {
// I need username and password body parameters value in controller
}
You would need something like Postman client in the browser.
You can install it from here. After installation, you can refer to answer to this question to know how to use it.

Categories