Feign multipart with Json request part - java

I have Feign client in one service with a method
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
MyDto uploadDocument(#RequestPart("file") MultipartFile file,
#RequestPart("myDto") String myDto);
I have a controller in another service
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<MyDto> uploadDocument(#RequestParam("file") MultipartFile file,
#RequestPart("myDto") MyDto myDto) {
.... some code here
}
The issue I faced is that Feign sends myDto with Content-type : text/plain and I have HttpMediaTypeNotSupportedException
Is it possible to send #RequestPart("myDto") String myDto with Content-type : application/json ?
expected Raw request:
----------------------------boundary
Content-Disposition: form-data; name="file"; filename="fileName"
<file>
----------------------------boundary
Content-Disposition: form-data; name="myDto"
**Content-Type: application/json**
{"myDto": ""}
Current raw request:
----------------------------boundary
Content-Disposition: form-data; name="file"; filename="fileName"
<file>
----------------------------boundary
Content-Disposition: form-data; name="myDto"
**Content-Type: text/plain**
{"myDto": ""}

Managed to solve this by replacing the feign-form PojoWriter. By default it's serializing each field of an object as a separate part.
#Bean
public Encoder feignEncoder () {
return new MyFormEncoder(objectMapper, new SpringEncoder(messageConverters));
}
public class MyFormEncoder extends SpringFormEncoder {
/**
* Constructor with specified delegate encoder.
*
* #param delegate delegate encoder, if this encoder couldn't encode object.
*/
public MyFormEncoder(ObjectMapper objectMapper, Encoder delegate) {
super(delegate);
val processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);
processor.addFirstWriter(new MyPojoWriter(objectMapper));
}
}
#FieldDefaults(level = PRIVATE, makeFinal = true)
public class MyPojoWriter extends AbstractWriter {
private ObjectMapper objectMapper;
public MyPojoWriter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#Override
public boolean isApplicable(Object object) {
return isUserPojo(object);
}
#Override
protected void write(Output output, String key, Object value) throws EncodeException {
var data = "";
try {
data = objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
}
val string = new StringBuilder()
.append("Content-Disposition: form-data; name=\"").append(key).append('"').append(CRLF)
.append("Content-Type: application/json; charset=").append(output.getCharset().name()).append(CRLF)
.append(CRLF)
.append(data)
.toString();
output.write(string);
}
private boolean isUserPojo(#NonNull Object object) {
val type = object.getClass();
val typePackage = type.getPackage();
return typePackage != null && typePackage.getName().startsWith("com.my-package.");
}
}

Update to 2021.
//spring-cloud-openfeign-core
import org.springframework.cloud.openfeign.support.JsonFormWriter;
#Import(JsonFormWriter.class)
public class MyConfig {
#Bean
Encoder feignEncoder(JsonFormWriter jsonFormWriter) {
return new SpringFormEncoder() {{
var processor = (MultipartFormContentProcessor) getContentProcessor(ContentType.MULTIPART);
processor.addFirstWriter(jsonFormWriter);
processor.addFirstWriter(new SpringSingleMultipartFileWriter());
processor.addFirstWriter(new SpringManyMultipartFilesWriter());
}};

You need to define bean JsonFormWriter in your feign client's configuration.
Here is an example of the client:
#FeignClient(
name = "my-client",
configuration = MyClientConfiguration.class
)
public interface MyClient {
#PostMapping(value = "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
void uploadFile(#RequestPart("request") MyFileUploadRequest request,
#RequestPart("file") MultipartFile file);
}
public class MyClientConfiguration {
#Bean
JsonFormWriter jsonFormWriter() {
return new JsonFormWriter();
}
}
And an example of the controller:
#RestController
public class FileUploadApi {
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void uploadFile(
#RequestPart("request") MyFileUploadRequest request,
#RequestPart("file") MultipartFile file) {
}
This feature was added in scope of this PR: https://github.com/spring-cloud/spring-cloud-openfeign/pull/314

Using the #PathVariable annotation and with a registered SpringFormEncoder you need to convert the "myDto" into a MultipartFile.
The client:
#PostMapping(value = "/files/upload", consumes = MULTIPART_FORM_DATA_VALUE)
MyDto uploadDocument(#PathVariable("file") MultipartFile file, #PathVariable("myDto") MultipartFile myDto)
The encoder:
#RequiredArgsConstructor
public class FeignClientConfiguration {
private final ObjectFactory<HttpMessageConverters> messageConverters;
//To support multipart file upload
#Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
Creating MultipartFile from the DTO:
public MultipartFile createMultipartFile(#NotNull MyDto myDto) throws JsonProcessingException {
return new org.springframework.mock.web.MockMultipartFile(
"fileName",
"originalFileName",
MediaType.APPLICATION_JSON.toString(),
objectMapper.writeValueAsBytes(myDto));
}
Why this solution with #PathVariable works is described here https://github.com/spring-cloud/spring-cloud-netflix/issues/867

Related

How can a spring controller deal with application/octet-stream request?

I wrote a spring controller with following methods to deal with a callback http request,
#PostMapping ("/test")
public void notifyTranscodeResult(String paramStr){
...
}
#PostMapping ("/test2")
public void notifyTranscodeResult(#RequestBody ParamClass param){
...
}
but I get errors: Resolved exception caused by handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
I can't change the callback http request because they are from other third-party services, how can I change my controller to correctly get the request params?
You need to define consumes attribute.
#PostMapping (path = "/test2", consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
Here is the implementation with Unit Test Case.
#PostMapping(value = "/upload",
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public String demo(HttpServletRequest httpServletRequest) {
ServletInputStream inputStream;
try {
inputStream = httpServletRequest.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
final List<String> list = new BufferedReader(new InputStreamReader(inputStream))
.lines().toList();
System.out.println(list);
return "Hello World";
}
Test Case
#Autowired
private MockMvc mockMvc;
#Test
public void shouldTestBinaryFileUpload() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders
.post("/api/user/upload")
.content("Hello".getBytes())
.contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(MockMvcResultMatchers
.status()
.isOk())
.andExpect(MockMvcResultMatchers
.content()
.bytes("Hello World".getBytes()));
}

How to add Location header to the http response?

I have a Java project and I'm using Servlet in order to handle http requests.
I also using Spring
When I receive a request to create a new object (for example an account), I would like also to return the “location” header with the GET URL of the newly created object.
for example: location: /accounts/1000
I understand the header are added to the Servlet filter (correct me if Im wrong)
public class ApiLogFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger("apilogger");
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = ((HttpServletResponse) servletResponse);
httpServletResponse.addHeader( "Location","the location value");
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
String queryString = httpServletRequest.getQueryString() != null ? httpServletRequest.getQueryString() : "N/A";
String logMessage = "URL: " + httpServletRequest.getRequestURL() + ", Query String: " + queryString + ", Response Status: " + httpServletResponse.getStatus() ;
LOGGER.info(logMessage);
}
}
#Override
public void destroy() {
}
}
But I don't understand how to get the location value from the API
#RequestMapping("/accounts")
public class IgnoreRuleController {
private AccountService accountService;
public void setIgnoreRuleService(IgnoreRuleService ignoreRuleService) {
this.accountService = ignoreRuleService;
}
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestBody Account account) {
return new Gson().toJson(accountService.createAccount(account));
}
}
I found solution here
http://learningviacode.blogspot.com/2013/07/post-with-location.html
you didn't need to do anything with the filter.
in the api itself:
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> createIgnoreRule(#RequestBody IgnoreRule ignoreRule) {
String response = new Gson().toJson(ignoreRuleService.createIgnoreRule(ignoreRule));
final URI location = ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/ignore_rules/{id}").build()
.expand(ignoreRule.getId()).toUri();
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(location);
final ResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.CREATED);
return entity;
}
It's very simple, you can pass the header directly throw your method signature:
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestHeader HttpHeaders httpHeader, #RequestBody Account account) {
var s = httpHeader.get("Location");
System.out.println(s.get(0));
return ...
}
In fact you can pass the whole request also which contains everything (Headers, Body, ...):
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(HttpServletRequest httpRequest, #RequestBody Account account) {
var s = httpRequest.getHeader("Location");
System.out.println(s);
return ....
}

What is the correct way to capture the Http Status code with Spring AOP?

I am creating an aspect to register my application using org.springframework.web.bind.annotation.RestController like #Pointcut, this works perfectly when my class responds normally, but when an exception occurs for some reason, the returned httpStatus is always 200, even If my http response returns 500 when an error occurs, I think this is because RestController does not set the http status, but delegates it to the exception handler, how do I fix this and still have traceability on top of the restcontroller?
Follow my rest controller
#Slf4j
#RestController
#RequestMapping("/api/conta")
public class ContaResourceHTTP {
#JetpackMethod("Pagamento de conta")
#PostMapping("/pagamento")
public void realizarPagamento(#RequestBody DTOPagamento dtoPagamento) throws InterruptedException
{
}
#JetpackMethod("Transferência entre bancos")
#PostMapping("/ted")
public void realizarTED(#RequestBody DTOPagamento dtoPagamento) throws java.lang.Exception
{
if(true)
throw new Exception("XXX");
//log.info(dtoPagamento.toString());
}
}
my AOP implementation:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass = true)
#Slf4j
public class MetricsAspect {
//#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
#Pointcut("execution(* javax.servlet.http.HttpServlet.*(..)) *)")
public void springBeanPointcut() {
}
#Autowired
Tracer tracer;
#Around("springBeanPointcut()")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
long inicioProcesso = System.currentTimeMillis();
joinPoint.proceed();
long finalProcesso = System.currentTimeMillis();
long duracaoProcesso = finalProcesso - inicioProcesso;
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
Metrics metricas = new Metrics();
metricas.setDuracaoMs(duracaoProcesso);
metricas.setDataHoraRequisicao(milissegundosToStringDate(inicioProcesso));
metricas.setDataHoraResposta(milissegundosToStringDate(finalProcesso));
metricas.setServidorOrigem(request.getRemoteAddr());
metricas.setPortaOrigem(request.getRemotePort());
metricas.setDominioAcesso(request.getLocalName());
metricas.setPortaAcesso(request.getLocalPort());
metricas.setUrlPath(request.getRequestURI());
metricas.setMetodoHttp(request.getMethod());
metricas.setIdTransacao(tracer.currentSpan().context().traceIdString());
metricas.setIdSpan(tracer.currentSpan().context().spanIdString());
metricas.setStatusHttp(response.getStatus());
log.info(JSONConversor.toJSON(metricas));
}
public String milissegundosToStringDate(long ms) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date dataInicial = new Date(ms);
return dateFormat.format(dataInicial);
}
}
My exception handler:
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionControllerAdvice {
#ExceptionHandler({ Throwable.class })
public ResponseEntity<ApiError> handlerValidationException2(Throwable e) {
return new ResponseEntity<>(new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, e, traceRespostaAPI),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
After a while I was able to solve the problem with a solution that may not be the most elegant for the problem, basically I used two pointcuts, one in the restcontroller to intercept the #JetpackMethod annotation value and add it to the http response header with advice before and another around HttpServlet that really is the one who really gets back with the modified http status.
Here's the code below that solved my problem.
This class intercepts annotation and adds its value to the header.
#Aspect
#Component
public class InterceptRestAnnotationAspect {
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllerExecution() {}
#Before("restControllerExecution()")
public void setMetodoHttpHeader(JoinPoint joinPoint) throws Throwable {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
String origem = VerificadorOrigem.processarOrigem(joinPoint);
response.setHeader("nomeMetodo", origem);
}
}
This other class logs the servlet metrics I needed and can retrieve the value entered in the header earlier.
#Aspect
#Component
#Slf4j
public class MetricsAspect {
#Pointcut("execution(* javax.servlet.http.HttpServlet.*(..)) *)")
public void servletService() {
}
#Autowired
Tracer tracer;
#Around("servletService()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
long inicioProcesso = System.currentTimeMillis();
Object result = joinPoint.proceed();
long finalProcesso = System.currentTimeMillis();
long duracaoProcesso = finalProcesso - inicioProcesso;
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getResponse();
Metrics metricas = new Metrics();
String funcionalidade = response.getHeader("nomeMetodo") == null ? "Indeterminada"
: response.getHeader("nomeMetodo");
metricas.setNivelLog("INFO");
metricas.setFuncionalidade(funcionalidade);
metricas.setDuracaoMs(duracaoProcesso);
metricas.setDataHoraRequisicao(ManipulaData.milissegundosToStringDate(inicioProcesso));
metricas.setDataHoraResposta(ManipulaData.milissegundosToStringDate(finalProcesso));
metricas.setServidorOrigem(request.getRemoteAddr());
metricas.setPortaOrigem(request.getRemotePort());
metricas.setDominioAcesso(request.getLocalName());
metricas.setPortaAcesso(request.getLocalPort());
metricas.setUrlPath(request.getRequestURI());
metricas.setMetodoHttp(request.getMethod());
metricas.setIdTransacao(tracer.currentSpan().context().traceIdString());
metricas.setIdSpan(tracer.currentSpan().context().spanIdString());
metricas.setStatusHttp(response.getStatus());
log.info(JSONConversor.toJSON(metricas));
return result;
}
}
I don't think the code after joinPoint.proceed(); gets executed in case of Exceptions.
You can have a different advice for execution in case of Exceptions:
#AfterThrowing(pointcut = "springBeanPointcut()", throwing = "e")
public void afterThrowingAdvice(JoinPoint jp, Exception e) {
....
}

Return empty JSON from spring controller for void response

I'm using Java 8, Tomcat 8, Spring-WebMVC 4.2.2.RELEASE, FasterXML 2.6.3.
I have the following method in my controller
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public void updateCurrentUserDetails(#RequestBody final UserDTO userDTO) {
final UserWithId user = SecurityUtil.getCurrentUser();
this.userAccountService.updateUserDetails(user.getUserId(), user.getUsername(), userDTO);
}
This method returns void which resolves in an empty (0 byte) response. However the clients connecting to the server always expect JSON reponses even, if its an empty response.
So I would like to configure Spring/Jackson to return {} (2 byte) in that case.
I already thought about returning new Object() everywhere in the calls that would return void otherwise but IMO this is a dirty soution and there must be something better.
There shouldn't be any need to do all that. You can just use a 204 response code, which is made for the situation you are describing. You don't even need the ResponseBody annotation, just use:
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateCurrentUserDetails(#RequestBody final UserDTO userDTO) {
final UserWithId user = SecurityUtil.getCurrentUser();
this.userAccountService.updateUserDetails(user.getUserId(), user.getUsername(), userDTO);
}
204 response code:
The 204 (No Content) status code indicates that the server has
successfully fulfilled the request and that there is no additional
content to send in the response payload body.
Its quite easy.
Just add the following to your spring xml/java config
<mvc:interceptors>
<bean class="de.st_ddt.util.VoidResponseHandlerInterceptor" />
</mvc:interceptors>
And add this class to your classpath
public class VoidResponseHandlerInterceptor extends HandlerInterceptorAdapter {
private static final String voidResponse = "{}";
#Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws IOException {
// Returned void?
if (!response.isCommitted()) {
// Used ModelAndView?
if (modelAndView != null) {
return;
}
// Access static resource?
if (DefaultServletHttpRequestHandler.class == handler.getClass()) {
return;
}
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
try (final Writer writer = response.getWriter()) {
writer.write(voidResponse);
}
response.flushBuffer();
}
}
}

Testing Spring MultipartHttpServletRequest

Trying to test a spring controller that we have for multiple file upload. Here is the controller:
#RequestMapping("/vocabularys")
#Controller
public class VocabularyController {
...
The action I want to test:
#RequestMapping(value = "/import", method = {RequestMethod.PUT, RequestMethod.POST})
#ResponseBody
#CacheEvict(value="vocabulary", allEntries=true)
public Object importVocabulary(MultipartHttpServletRequest request, HttpServletResponse response) {
...
The resolver I have in the webmvc-config.xml:
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
The code works just fine and all. I'm running into problems when I am trying to unit/integration test this.
Here is my attempt at the test:
public class VocabularyControllerTest extends BaseControllerTest {
static final private String AdminUsername = "administrator";
#Test
public void shouldBeAbleToUploadAFile() throws Exception {
createTestWorkspace();
login(AdminUsername, "*");
MockMultipartFile file = new MockMultipartFile("test_vocab.xml", new FileInputStream("src/test/files/acme_vocabulary.xml"));
MockMultipartHttpServletRequestBuilder mockMultipartHttpServletRequestBuilder = (MockMultipartHttpServletRequestBuilder) fileUpload("/vocabularys/import").accept(MediaType.ALL).session(httpSession);
mockMultipartHttpServletRequestBuilder.file(file);
mockMultipartHttpServletRequestBuilder.content("whatever");
ResultActions resultActions = mockMvc.perform(mockMultipartHttpServletRequestBuilder);
resultActions.andExpect(status().isFound());
}
}
Ignore the createWorkspace() and login() and stuff - those are for passing through some security filters.
The relevant part of the BaseControllerTest:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(locations = {
"file:src/test/resources/META-INF/spring/applicationContext.xml",
"file:src/test/resources/META-INF/spring/applicationContext-security.xml",
"file:src/main/resources/META-INF/spring/applicationContext-database.xml",
"file:src/main/resources/META-INF/spring/applicationContext-activiti.xml",
"file:src/main/resources/META-INF/spring/applicationContext-cache.xml",
"file:src/main/resources/META-INF/spring/applicationContext-jms.xml",
"file:src/main/resources/META-INF/spring/applicationContext-mail.xml",
"file:src/main/resources/META-INF/spring/applicationContext-mongo.xml"}),
#ContextConfiguration(locations = {
"file:src/main/webapp/WEB-INF/spring/webmvc-config.xml",
"file:src/test/webapp/WEB-INF/spring/applicationContext-filters.xml"})
})
#Transactional
public class BaseControllerTest extends BaseTest {
#Autowired
WebApplicationContext wac;
#Autowired
MockHttpSession httpSession;
#Autowired
MockServletContext servletContext;
#Autowired
OpenEntityManagerInViewFilter openEntityManagerInViewFilter;
#Autowired
HiddenHttpMethodFilter hiddenHttpMethodFilter;
#Autowired
CharacterEncodingFilter characterEncodingFilter;
#Autowired
SessionFilter sessionFilter;
#Autowired
WorkflowAsSessionFilter workflowAsSessionFilter;
#Autowired
FilterChainProxy springSecurityFilterChain;
#Autowired
RequestFilter requestFilter;
MockMvc mockMvc;
protected static final String TestFileDir = "src/test/files/";
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(openEntityManagerInViewFilter, "/*")
.addFilter(hiddenHttpMethodFilter, "/*")
.addFilter(characterEncodingFilter, "/*")
.addFilter(sessionFilter, "/*")
.addFilter(workflowAsSessionFilter, "/*")
.addFilter(springSecurityFilterChain, "/*")
.addFilter(requestFilter, "/*")
.build();
servletContext.setContextPath("/");
Session session = Session.findBySessionId(httpSession.getId());
if (session == null) {
session = new Session();
session.setJsessionid(httpSession.getId());
session.persist();
}
}
...
The issue is that when I try debugging this, the perform action on the mockMvc object never hits my controller method. I thought it was an issue getting past our security filters (which is why I have all the login and stuff) but I tested other actions in the vocabulary controller and I am able to hit them just fine.
Thoughts? Ideas? Suggestions?
Alright, found the issue.
Spring's MockMultipartHttpServletRequestBuilder returns a MockHttpMultipartServletRequest object eventually.
What the browser does however is post a multipart-encoded request which then gets picked up and parsed by the CommonsMultipartResolver bean defined in the XML.
In the test however, since we are already posting a MockHttpMultipartServletRequest, we don't want the resolver parsing this, so all we got to do is have a profile where the resolver doesn't kick in.
What we have chosen to do however is end up constructing a MockHttpServletRequest that has multipart encoding and put it through the Spring filters so that we can also integration test the resolver kicking in.
Unfortunately I don't see any support/helper in the Spring testing lib which allows you to take a MockHttpServletRequest and addPart() to it, or something to that effect => handcoded browser emulation function :(
The simple way how to test multipart upload is use StandardServletMultipartResolver.
and for test use this code:
final MockPart profilePicture = new MockPart("profilePicture", "stview.jpg", "image/gif", "dsdsdsd".getBytes());
final MockPart userData = new MockPart("userData", "userData", "application/json", "{\"name\":\"test aida\"}".getBytes());
this.mockMvc.perform(
fileUpload("/endUsers/" + usr.getId().toString()).with(new RequestPostProcessor() {
#Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.addPart(profilePicture);
request.addPart(userData);
return request;
}
})
MockPart class
public class MockPart extends MockMultipartFile implements Part {
private Map<String, String> headers;
public MockPart(String name, byte[] content) {
super(name, content);
init();
}
public MockPart(String name, InputStream contentStream) throws IOException {
super(name, contentStream);
init();
}
public MockPart(String name, String originalFilename, String contentType, byte[] content) {
super(name, originalFilename, contentType, content);
init();
}
public MockPart(String name, String originalFilename, String contentType, InputStream contentStream) throws IOException {
super(name, originalFilename, contentType, contentStream);
init();
}
public void init() {
this.headers = new HashMap<String, String>();
if (getOriginalFilename() != null) {
this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"; filename=\"" + getOriginalFilename() + "\"");
} else {
this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"");
}
if (getContentType() != null) {
this.headers.put("Content-Type".toLowerCase(), getContentType());
}
}
#Override
public void write(String fileName) throws IOException {
}
#Override
public void delete() throws IOException {
}
#Override
public String getHeader(String name) {
return this.headers.get(name.toLowerCase());
}
#Override
public Collection<String> getHeaders(String name) {
List<String> res = new ArrayList<String>();
if (getHeader(name) != null) {
res.add(getHeader(name));
}
return res;
}
#Override
public Collection<String> getHeaderNames() {
return this.headers.keySet();
}
}

Categories

Resources