Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
beba48b
Add Pojo and failing test to FeignClientTests
darrenfoong Mar 27, 2020
b629e13
Add SpringPojoFormEncoder
darrenfoong Mar 28, 2020
59df0b6
Fix configuration
darrenfoong Mar 28, 2020
f965475
Update Pojo
darrenfoong Mar 28, 2020
93b7f80
Implement multipartPojo
darrenfoong Mar 28, 2020
a8b7cb0
Does not work if Pojo has one field: this is crazy
darrenfoong Mar 28, 2020
ce3b96f
Disable expansion for multipart/form-data
darrenfoong Mar 28, 2020
b66c3ba
Remove Pojo
darrenfoong Mar 28, 2020
9cdfc49
Tidy code
darrenfoong Mar 29, 2020
1a69560
Add testSinglePojoRequestPart
darrenfoong Mar 29, 2020
1a917e2
Update testMultiplePojoRequestPart
darrenfoong Mar 29, 2020
0e3b5fa
Update testMultiplePojoRequestPart
darrenfoong Mar 29, 2020
5312520
Add testRequestPartWithListOfPojosAndListOfMultipartFiles
darrenfoong Mar 29, 2020
a7d98fe
Fix Checkstyle errors
darrenfoong Mar 29, 2020
c8c1f0b
Update license year and authors
darrenfoong Mar 29, 2020
e33e0fd
Tidy isApplicable
darrenfoong Mar 29, 2020
d18b7ab
Refactor isApplicable
darrenfoong Mar 29, 2020
974bf01
Rename classes and methods
darrenfoong Mar 29, 2020
75066f9
Change variable names
darrenfoong Mar 29, 2020
9a9112f
Rename PojoFormWriter to AbstractFormWriter
darrenfoong Apr 9, 2020
1b515cb
Rename method
darrenfoong Apr 9, 2020
a5e07ba
Use ObjectProvider
darrenfoong Apr 10, 2020
33a279b
Change constructor of SpringEncoder to move SpringPojoFormEncoder to …
darrenfoong Apr 10, 2020
93050f3
Rename variables
darrenfoong Apr 10, 2020
dcc120e
Remove unused variable
darrenfoong Apr 11, 2020
a1440a4
Extract springEncoder() method
darrenfoong Apr 11, 2020
3b7f9b2
Restore previous constructor
darrenfoong Apr 11, 2020
1799ade
Extract isMultipartFormData() method
darrenfoong Apr 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,6 +40,7 @@
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.cloud.openfeign.support.PojoFormWriter;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
Expand All @@ -54,6 +55,7 @@
/**
* @author Dave Syer
* @author Venil Noronha
* @author Darren Foong
*/
@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
Expand All @@ -73,6 +75,9 @@ public class FeignClientsConfiguration {
@Autowired(required = false)
private SpringDataWebProperties springDataWebProperties;

@Autowired(required = false)
private PojoFormWriter pojoFormWriter;

@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
Expand All @@ -84,15 +89,29 @@ public Decoder feignDecoder() {
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
if (this.pojoFormWriter != null) {
return new SpringEncoder(this.pojoFormWriter, this.messageConverters);
}
else {
return new SpringEncoder(this.messageConverters);
}
}

@Bean
@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
@ConditionalOnMissingBean
public Encoder feignEncoderPageable() {
PageableSpringEncoder encoder = new PageableSpringEncoder(
new SpringEncoder(this.messageConverters));
PageableSpringEncoder encoder;

if (this.pojoFormWriter != null) {
encoder = new PageableSpringEncoder(
new SpringEncoder(this.pojoFormWriter, this.messageConverters));
}
else {
encoder = new PageableSpringEncoder(
new SpringEncoder(this.messageConverters));
}

if (springDataWebProperties != null) {
encoder.setPageParameter(
springDataWebProperties.getPageable().getPageParameter());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.openfeign.support;

import java.io.IOException;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

/**
* @author Darren Foong
*/
@Component
public class JsonPojoFormWriter extends PojoFormWriter {

@Autowired
private ObjectMapper objectMapper;

@Override
protected MediaType getContentType() {
return MediaType.APPLICATION_JSON;
}

@Override
protected String writePojoAsString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.openfeign.support;

import java.io.IOException;
import java.util.Iterator;
import java.util.function.Predicate;

import feign.codec.EncodeException;
import feign.form.multipart.AbstractWriter;
import feign.form.multipart.Output;
import feign.form.util.PojoUtil;

import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

import static feign.form.ContentProcessor.CRLF;
import static feign.form.util.PojoUtil.isUserPojo;

/**
* @author Darren Foong
*/
public abstract class PojoFormWriter extends AbstractWriter {

@Override
public boolean isApplicable(Object object) {
return !isTypeOrCollection(object, o -> o instanceof MultipartFile)
&& isTypeOrCollection(object, PojoUtil::isUserPojo);
}

@Override
public void write(Output output, String key, Object object) throws EncodeException {
try {
String string = new StringBuilder()
.append("Content-Disposition: form-data; name=\"").append(key)
.append('"').append(CRLF).append("Content-Type: ")
.append(getContentType()).append("; charset=")
.append(output.getCharset().name()).append(CRLF).append(CRLF)
.append(writePojoAsString(object)).toString();

output.write(string);
}
catch (IOException e) {
throw new EncodeException(e.getMessage());
}
}

protected abstract MediaType getContentType();

protected abstract String writePojoAsString(Object object) throws IOException;

private boolean isTypeOrCollection(Object object, Predicate<Object> isType) {
if (object.getClass().isArray()) {
Object[] array = (Object[]) object;

return array.length > 1 && isType.test(array[0]);
}
else if (object instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) object;
Iterator<?> iterator = iterable.iterator();

return iterator.hasNext() && isType.test(iterator.next());
}
else {
return isType.test(object);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -54,16 +54,24 @@
* @author Scien Jus
* @author Ahmad Mozafarnia
* @author Aaron Whiteside
* @author Darren Foong
*/
public class SpringEncoder implements Encoder {

private static final Log log = LogFactory.getLog(SpringEncoder.class);

private final SpringFormEncoder springFormEncoder = new SpringFormEncoder();
private SpringFormEncoder springFormEncoder;

private final ObjectFactory<HttpMessageConverters> messageConverters;

public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
this.springFormEncoder = new SpringFormEncoder();
this.messageConverters = messageConverters;
}

public SpringEncoder(PojoFormWriter writer,
ObjectFactory<HttpMessageConverters> messageConverters) {
this.springFormEncoder = new SpringPojoFormEncoder(writer);
this.messageConverters = messageConverters;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import feign.Contract;
import feign.Feign;
Expand All @@ -42,6 +43,7 @@
import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestPartParameterProcessor;
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
Expand All @@ -54,6 +56,7 @@
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -72,6 +75,7 @@
* @author Olga Maciaszek-Sharma
* @author Aaron Whiteside
* @author Artyom Romanenko
* @author Darren Foong
*/
public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware {
Expand Down Expand Up @@ -272,6 +276,17 @@ private void checkOne(Method method, Object[] values, String fieldName) {
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data,
Annotation[] annotations, int paramIndex) {
boolean isMultipartFormData = false;

Collection<String> contentTypes = data.template().headers()
.get(HttpEncoding.CONTENT_TYPE);

if (contentTypes != null && !contentTypes.isEmpty()) {
String type = contentTypes.iterator().next();
isMultipartFormData = Objects.equals(MediaType.valueOf(type),
MediaType.MULTIPART_FORM_DATA);
}

boolean isHttpAnnotation = false;

AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
Expand All @@ -291,7 +306,8 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data,
}
}

if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
if (!isMultipartFormData && isHttpAnnotation
&& data.indexToExpander().get(paramIndex) == null) {
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
if (this.conversionService.canConvert(typeDescriptor,
STRING_TYPE_DESCRIPTOR)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.openfeign.support;

import feign.form.MultipartFormContentProcessor;
import feign.form.spring.SpringFormEncoder;

import static feign.form.ContentType.MULTIPART;

/**
* @author Darren Foong
*/
public class SpringPojoFormEncoder extends SpringFormEncoder {

public SpringPojoFormEncoder(PojoFormWriter pojoFormWriter) {
super();

MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(
MULTIPART);
processor.addFirstWriter(pojoFormWriter);
}

}
Loading