How to send POST request by Spring cloud Feign - spring-cloud-netflix

It's my Feign interface
#FeignClient(
name="mpi",
url="${mpi.url}",
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> getPAReq(#QueryMap Map<String, String> queryMap
);
}
and my custom configuration
public class FeignSimpleEncoderConfig {
public static final int FIVE_SECONDS = 5000;
#Bean
public Logger.Level feignLogger() {
return Logger.Level.FULL;
}
#Bean
public Request.Options options() {
return new Request.Options(FIVE_SECONDS, FIVE_SECONDS);
}
#Bean
#Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder()
.encoder(new FormEncoder());
}
}
If I send request like this I see that my request send Content-Type: application/json;charset=UTF-8.
But if I set content type
consumes = "application/x-www-form-urlencoded"
I've this error message
feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [application/x-www-form-urlencoded]
at org.springframework.cloud.netflix.feign.support.SpringEncoder.encode(SpringEncoder.java:108) ~[spring-cloud-netflix-core-1.1.7.RELEASE.jar:1.1.7.RELEASE]
How to send POST request, I think I should make something more with Encoder.
Thanks for your help.

First of all you should change your Feign interface like this:
#FeignClient (
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
ResponseEntity<String> getPAReq(Map<String, ?> queryMap);
}
Then you should set the encoder during feign configuration:
public class FeignSimpleEncoderConfig {
#Bean
public Encoder encoder() {
return new FormEncoder();
}
}

It seems to me that Map is not valid for form body. MultiValueMap works just fine.
Feign client:
#FeignClient(name = "name", url="url", configuration = FromUrlEncodedClientConfiguration.class)
public interface PayPalFeignClient {
#RequestMapping(value = "/", method = POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#Headers("Content-Type: application/x-www-form-urlencoded")
String foo(MultiValueMap<String, ?> formParams);
}
Config:
#Configuration
public class FromUrlEncodedClientConfiguration {
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Bean
#Primary
#Scope(SCOPE_PROTOTYPE)
Encoder feignFormEncoder() {
return new FormEncoder(new SpringEncoder(this.messageConverters));
}
}
Gradle dependencies:
compile group: 'io.github.openfeign.form', name: 'feign-form', version: '2.0.2'
compile group: 'io.github.openfeign.form', name: 'feign-form-spring', version: '2.0.5'
After that all you have to do is call it with a MultivalueMap parameter.

Specify the correct encoder for handle form encoded request
you can specify multi encoder example json/xml/formhttpurl encoded
#Bean
public Encoder feignEncoder() {
ObjectFactory<HttpMessageConverters> objectFactory = () ->
new HttpMessageConverters(new FormHttpMessageConverter());
return new SpringEncoder(objectFactory);
}
Important FormHttpMessageConverter serialize only MultiValueMap subsclass

im my case it was due to outdated version of lombok, updating to 1.18.16 resolve it

Related

How to send the Multipart file and json data to spring boot

I have the POST request api call to accept the json body request parameters and multipart file from client side(postman or java client).
I want to pass both the json data and multipart file in single request.
I have written the code like below.
#RequestMapping(value = "/sendData", method = RequestMethod.POST, consumes = "multipart/form-data")
public ResponseEntity<MailResponse> sendMail(#RequestPart MailRequestWrapper request) throws IOException
But, i could not accomplish it using postman rest client.
I'm using spring boot on server side.
Could anyone suggest me on this question.
Thanks in advance,
You cat use #RequestParam and Converter for JSON objects
simple example :
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
#Data
public static class User {
private String name;
private String lastName;
}
#Component
public static class StringToUserConverter implements Converter<String, User> {
#Autowired
private ObjectMapper objectMapper;
#Override
#SneakyThrows
public User convert(String source) {
return objectMapper.readValue(source, User.class);
}
}
#RestController
public static class MyController {
#PostMapping("/upload")
public String upload(#RequestParam("file") MultipartFile file,
#RequestParam("user") User user) {
return user + "\n" + file.getOriginalFilename() + "\n" + file.getSize();
}
}
}
and postman:
UPDATE
apache httpclient 4.5.6 example:
pom.xml dependency:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.6</version>
</dependency>
<!--dependency for IO utils-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
service will be run after application fully startup, change File path for your file
#Service
public class ApacheHttpClientExample implements ApplicationRunner {
private final ObjectMapper mapper;
public ApacheHttpClientExample(ObjectMapper mapper) {
this.mapper = mapper;
}
#Override
public void run(ApplicationArguments args) {
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
File file = new File("yourFilePath/src/main/resources/foo.json");
HttpPost httpPost = new HttpPost("http://localhost:8080/upload");
ExampleApplication.User user = new ExampleApplication.User();
user.setName("foo");
user.setLastName("bar");
StringBody userBody = new StringBody(mapper.writeValueAsString(user), MULTIPART_FORM_DATA);
FileBody fileBody = new FileBody(file, DEFAULT_BINARY);
MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.addPart("user", userBody);
entityBuilder.addPart("file", fileBody);
HttpEntity entity = entityBuilder.build();
httpPost.setEntity(entity);
HttpResponse response = client.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
// print response
System.out.println(IOUtils.toString(responseEntity.getContent(), UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
}
}
console output will look like below:
ExampleApplication.User(name=foo, lastName=bar)
foo.json
41
You can use both of them.
#RequestPart : This annotation associates a part of a multipart request with the method argument, which is useful for sending complex multi-attribute data as payload, e.g., JSON or XML.
In other words Request Part parse your json string object from request to your class object. On the other hand, Request Param just obtain the string value from your json string value.
For example, using Request Part:
#RestController
#CrossOrigin(origins = "*", methods= {RequestMethod.POST, RequestMethod.GET,
RequestMethod.PUT})
#RequestMapping("/api/api-example")
public class ExampleController{
#PostMapping("/endpoint-example")
public ResponseEntity<Object> methodExample(
#RequestPart("test_file") MultipartFile file,
#RequestPart("test_json") ClassExample class_example) {
/* do something */
}
}
and postman would be configured like:
#RequestParam : Another way of sending multipart data is to use #RequestParam. This is especially useful for simple data, which is sent as key/value pairs along with the file, as I said, just key/value. Also is used to get value from query params, I think that is its main goal.
I was stuck with this problem for past few hours
So I came across this question.
Summary:
Use #ModelAttribute instead of #RequestBody.
#ModelAttriute will work just like other normal(without multipart property in entity) Entity mapping.
You have two options -
Send a MultipartFile along with json data
public void uploadFile(#RequestParam("identifier") String identifier, #RequestParam("file") MultipartFile file){
}
OR
Send Json data inside a MultipartFile and then parse Multipart file as mentioned below and thats it.
public void uploadFile(#RequestParam("file") MultipartFile file){
POJO p = new ObjectMapper().readValue(file.getBytes(), POJO.class);
}
I explain all here in answer part:
enter link description here

spring boot: change print console to json rest

Actually, my project in Spring send values from the database via console, like this:
Console image, but I want to send this values via JSON like a rest API, but I don't know how to change that.
{
"depositarios": {
"correo": "correo",
"nombre": "nombre",
"numTel": "numTel",
"pApellido": "pApellido",
"SApellido": "sAellido"
}
}
this is my main class:
#SpringBootApplication
#ComponentScan("com.abner.springpostgresql.service.impl, com.abner.springpostgresql.dao.imp")
public class SpringPostgresqlApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
depoService.loadAllDepo();
}
}
this is my entired project source https://github.com/abnercoronado/restpostgresql
You have to create a RestController using the #RestController annotation like this:
#RestController
public class MyRestController {
#RequestMapping(value = "/personas", method = RequestMethod.GET)
public List<Persona> listaPersonas() {
// This is just a sample. Here you could bring your data form your db
List<Persona> lista = new ArrayList<Persona>();
Persona p = new Persona();
p.setNombre("angel");
p.setEdad(20);
lista.add(p);
return lista;
}
}
The value of the #RequestMapping annotation ("/personas" in this example) will be the endpoint. So when you access to the endpoint http://localhost:8080/personas (asuming that your app is running on http://localhost:8080) then you will get your data as json.
Here is an example of how to do it.
Here is another example (en espaƱol) that could help you.
You can use ObjectMapper to convert your pojo or object to JSON String and send where ever you wanted using thier API or anything.
Or you can create Rest Method and Access the API would return you the Json Value.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<?> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<?> lista = depoService.loadAllDepo();
return lista;
}
Another way of doing.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<Depo> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<Depo> lista = depoService.loadAllDepo();
return lista;
}
Once you start your server, you can run this by doing localhost:8080/depo. you can also return XML.

Add java List in JsonObject return value

I want to add a list in the json return value. Here is my code -
#RequestMapping(value = "/servicearea", produces="application/json")
#ResponseBody
public String ServiceArea(Model model, HttpServletRequest req, HttpServletResponse res)
{
List<ServiceArea> serviceLists = locationService.getAllServiceArea();
JsonObject result = Json.createObjectBuilder()
.add("message", "test")
.add("serviceLists", serviceLists)
.build();
return result.toString();
}
'serviceLists' is the one that I want to add. I'm getting error in this line - .add("serviceLists", serviceLists). Error message is that JsonObjectBuilder is not applicable for the arguments.
Thanks in advance.
Just create a pojo like this
class ReturnPojo {
private String message;
private List<ServiceArea> serviceLists;
//getters and setters
}
And from your controller you can do
ReturnPojo returnPojo = new ReturnPojo;
returnPojo.setMessage("test");
returnPojo.setServiceLists(serviceLists);
return returnPojo;
Change the method signature to return ReturnPojo
Below solution is only useful if you are using Rest API and Spring.
create class ServiceListPojo:
public class ServiceListPOJO{
List<ServiceArea> serviceLists;
public void setServiceLists(List<ServiceArea> serviceLists){
this.serviceLists = serviceLists;
}
}
Annotate your controller with #RestController
#RestController
public class SomeController{
#RequestMapping(value = "/servicearea")
public ServiceListPOJO ServiceArea()
{
List<ServiceArea> serviceLists = locationService.getAllServiceArea();
ServiceListPOJO slp = new ServiceListPOJO();
slp.setServciceLists(serviceLists);
return slp;
}
}

How to return JSON object in resolveException method of HandlerExceptionResolver in Spring MVC?

While implementing a File Uploader controller in Spring MVC I stucked with one problem. My code snap is given below.
#Controller
public class FileUploader extends AbstractBaseController implements HandlerExceptionResolver
{
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public JSONObject handleFileUpload(#RequestParam("file") MultipartFile file)
{
JSONObject returnObj = new JSONObject();
if (file.isEmpty())
{
returnObj.put("success", "false");
returnObj.put("message", "File is empty");
}
else
{
try
{
//my file upload logic goes here
}
catch (Exception e)
{
returnObj.put("success", "false");
returnObj.put("message", "File not uploaded.");
}
}
return returnObj;
}
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception exception)
{
ModelAndView model = new ModelAndView();
Map map = new HashMap();
if (exception instanceof MaxUploadSizeExceededException)
{
// I want to return JSONObject from here like given below.
/**
* { "message":"File size exceeded", "success":"false" }
* */
map.put("message", "File size exceeded");
map.put("success", "false");
model.addObject(map);
}
return model;
}
}
and my spring configuration look likes
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="maxUploadSize" value="300000"/>
</bean>
now In my controller I want to return JSONObject instead of ModelAndView in resolveException method in my controller as given in code snap because I am developing some like REST method to upload file.
any ideas?
Thanks
If you use the Spring 3.2 above, I recommend this way.
At first, declare the ControllerAdvice.
#Controller
#ControllerAdvice
public class JAttachfileApi extends BaseApi
And make the Exception Handler to response JSON Object as following.
#ExceptionHandler(MaxUploadSizeExceededException.class)
public #ResponseBody Map<String,Object> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex)
{
Map<String,Object> result = getResult();
JFileUploadJsonResponse errorResult = new JFileUploadJsonResponse();
errorResult.setError("Maximum upload size of "+ex.getMaxUploadSize()+" bytes exceeded.");
List<JFileUploadJsonResponse> resultData = new ArrayList<JFileUploadJsonResponse>();
resultData.add(errorResult);
result.put("files", resultData);
return result;
}
You simply can annotate the method resolveException as #ExceptionHandler() and then you can have its signature like any other controller method. So placing #ResponseBody before the return type should work.
"Much like standard controller methods annotated with a #RequestMapping annotation, the method arguments and return values of #ExceptionHandler methods can be flexible. For example, the HttpServletRequest can be accessed in Servlet environments and the PortletRequest in Portlet environments. The return type can be a String, which is interpreted as a view name, a ModelAndView object, a ResponseEntity, or you can also add the #ResponseBody to have the method return value converted with message converters and written to the response stream."

WcfFacility and Sequence contains no elements error?

I have wcf library with service contracts and implementations.
[ServiceContract]
public interface IServiceProtoType
{
[OperationContract]
Response GetMessage(Request request);
[OperationContract]
String SayHello();
}
[DataContract]
public class Request
{
private string name;
[DataMember]
public string Name
{
get { return name; }
set { name = value; }
}
}
[DataContract]
public class Response
{
private string message;
[DataMember]
public string Message
{
get { return message; }
set { message = value; }
}
}
public class MyDemoService : IServiceProtoType
{
public Response GetMessage(Request request)
{
var response = new Response();
if (null == request)
{
response.Message = "Error!";
}
else
{
response.Message = "Hello, " + request.Name;
}
return response;
}
public string SayHello()
{
return "Hello, World!";
}
}
I have windows service project that references this library, where MyService is just an empty shell that inherits ServiceBase. This service is installed and running under local system.
static void Main()
{
ServiceBase.Run(CreateContainer().Resolve());
}
private static IWindsorContainer CreateContainer()
{
IWindsorContainer container = new WindsorContainer();
container.Install(FromAssembly.This());
return container;
}
public class ServiceInstaller : IWindsorInstaller
{
#region IWindsorInstaller Members
public void Install(IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
string myDir;
if (string.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
{
myDir = AppDomain.CurrentDomain.BaseDirectory;
}
else
{
myDir = AppDomain.CurrentDomain.RelativeSearchPath;
}
var wcfLibPath = Path.Combine(myDir , "WcfDemo.dll");
string baseUrl = "http://localhost:8731/DemoService/{0}";
AssemblyName myAssembly = AssemblyName.GetAssemblyName(wcfLibPath);
container
.Register(
AllTypes
.FromAssemblyNamed(myAssembly.Name)
.InSameNamespaceAs<WcfDemo.MyDemoService>()
.WithServiceDefaultInterfaces()
.Configure(c =>
c.Named(c.Implementation.Name)
.AsWcfService(
new DefaultServiceModel()
.AddEndpoints(WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(baseUrl,
c.Implementation.Name)
)))), Component.For<ServiceBase>().ImplementedBy<MyService>());
}
#endregion
}
In Client Console app I have the following code and I am getting the following error:
{"Sequence contains no elements"}
static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer();
string baseUrl = "http://localhost:8731/DemoService/{0}";
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
Types
.FromAssemblyContaining<IServiceProtoType>()
.InSameNamespaceAs<IServiceProtoType>()
.Configure(
c => c.Named(c.Implementation.Name)
.AsWcfClient(new DefaultClientModel
{
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(baseUrl,
c.Name.Substring(1)))
})));
var service1 = container.Resolve<IServiceProtoType>();
Console.WriteLine(service1.SayHello());
Console.ReadLine();
}
I have an idea what this may be but you can stop reading this now (and I apologize for wasting your time in advance) if the answer to the following is no:
Is one (or more) of Request, Response, or MyDemoService in the same namespace as IServiceProtoType?
I suspect that Windsor is getting confused about those, since you are doing...
Types
.FromAssemblyContaining<IServiceProtoType>()
.InSameNamespaceAs<IServiceProtoType>()
... and then configuring everything which that returns as a WCF client proxy. This means that it will be trying to create proxies for things that should not be and hence a Sequence Contains no Elements exception (not the most useful message IMHO but crushing on).
The simple fix would be just to put your IServiceProtoType into its own namespace (I often have a namespace like XXXX.Services for my service contracts).
If that is not acceptable to you then you need to work out another way to identify just the service contracts - take a look at the If method for example or just a good ol' Component.For perhaps.