Unable to test the REST API developed with Spring Boot - rest

I am trying to test the REST API created with Spring Boot. Following is the signature of the method:
#RequestMapping(consumes = "multipart/form-data", method = RequestMethod.POST)
public Response<String> upload(#RequestBody CsvUploadModel form) {
Following is the details of Model Object:
private char separator;
private char quoteCharacter;
private String metricName;
private String groupName;
private MultipartFile file;
//getters and setters
I have tried accessing this service using 1. chrome Postman and 2. Simple http POST form. Every time I am getting the error: 415 : Unsupported media type.
EDIT:
Following is the bean configuration for multi part bean:
/**
* Allow file uploads
*
* #return
*/
#Bean
public MultipartConfigElement multipartConfigElement() {
MultiPartConfigFactory factory = new MultiPartConfigFactory();
factory.setMaxFileSize("500MB");
factory.setMaxRequestSize("500MB");
return factory.createMultipartConfig();
}
/**
* Get the multipart resolver
*
* #return
*/
#Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
I tried changing #RequestBody to #RequestParam but it didn't work. Following is the request preview of postman.
POST /dev/wizard/upload HTTP/1.1
Host: localhost:10022
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="metricName"
test
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="separator"
,
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Am I missing anything obvious?
Thanks

#RequestBody CsvUploadModel form
This requires a HttpMessageConverter to be present that can read request payloads of the type multipart/form-data. Unfortunately Spring currently does not provide such a converter. There is a FormHttpMessageConverter, but that can only read simple form data (application/x-www-form-urlencoded).
In order to get your method working you should remove the #RequestBody annotation and add a parameter for the files:
upload(CsvUploadModel form, #RequestParameter(required=false) MultipartFile file)
#RequestBody is not needed for binding form data. You then have to set the file manually:
form.setFile(file);
Maybe there's a third-party converter that supports reading multipart/form-data. Neither do I use nor know any.

Try retrofit
<dependency>
<groupId>com.squareup.retrofit</groupId>
<artifactId>retrofit</artifactId>
<version>1.6.1</version>
</dependency>
.
import retrofit.http.Body;
import retrofit.http.POST;
public interface IRestController {
#POST("/api-name")
public Response api(#Body Request request);
}
.
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import retrofit.RestAdapter;
public class TestRestAPI {
private static final String SERVER = "http://localhost:8080";
private IRestController service = new RestAdapter.Builder()
.setEndpoint(SERVER).build()
.create(IRestController.class);
#Test
public void basicTest(){
Response response = service.api(new Request());
assertNotNull(response);
}
}

Related

415 Unsupported media type on java rest service when uploading file

I am trying to build a rest service to upload a file. Here is what I've got. When I test in postman, I have form-data selected with "file" set to the file that I'm uploading.
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(#RequestParam("file") MultipartFile file)
My understanding is that I need the jersey-media-multipart dependency
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.25.1</version>
</dependency>
When I try to make the post request, I get a 415 Unsupported Media Type error, and I am not sure why. It is set to consume MULTIPART_FORM_DATA and the parameter is a MultipartFile.
EDIT I've added a configuration as follows,
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(MultiPartFeature.class);
}
}
I've also fixed my resource
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(
#FormDataParam("file") InputStream file,
#FormDataParam("file") FormDataContentDisposition fdcd,
#FormDataParam("file-detail") String fileDetail) throws IOException {
And now I get [[FATAL] No injection source found for a parameter of type public javax.ws.rs.core.Response
Here is my spring boot class, which contains additional configuration
#SpringBootApplication
#EnableCircuitBreaker
#EntityScan("com.whatever.")
#ImportResource({"classpath*:/META-INF/**/spring-bootstrap.xml"})
#Import({JerseyConfig.class})
#EnableJpaRepositories(basePackages="whatever", entityManagerFactoryRef="entityManagerFactory")
public class Application {
protected TomcatEmbeddedServletContainerFactory createTomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
((StandardHost) tomcat.getHost()).setErrorReportValveClass(StringUtils.EMPTY);
return super.getTomcatEmbeddedServletContainer(tomcat);
}
};
}
#Bean
public FilterRegistrationBean requestContextFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new org.springframework.web.filter.RequestContextFilter());
filterRegistration.setName("RequestContextFilter");
filterRegistration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
filterRegistration.addUrlPatterns(BASE_PATH + "*");
return filterRegistration;
}
#Bean
public FilterRegistrationBean springSecurityFilterChain() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new org.springframework.web.filter.DelegatingFilterProxy());
filterRegistration.setName("SpringSecurityFilterChain");
filterRegistration.setOrder(Ordered.HIGHEST_PRECEDENCE + 2);
filterRegistration.addUrlPatterns(BASE_PATH + "*");
return filterRegistration;
}
#Bean
#Conditional(NonWindowsCondition.class)
public FilterRegistrationBean f5Filter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new F5Filter());
filterRegistration.setName("F5Filter");
filterRegistration.setOrder(Ordered.HIGHEST_PRECEDENCE + 3);
filterRegistration.addUrlPatterns(BASE_PATH + "*");
return filterRegistration;
}
/**
* Initializes and registers the JAX-RS filter implementation, currently Jersey.
*
* #return The JAX-RS filter registration.
* #throws ClassNotFoundException
* #throws IllegalAccessException
* #throws InstantiationException
*/
#Bean
public FilterRegistrationBean jaxrsFilter() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Filter filter = (Filter) Class.forName("org.glassfish.jersey.servlet.ServletContainer").newInstance();
jerseyFilterRegistration.setFilter(filter);
jerseyFilterRegistration.setName("JerseyFilter");
jerseyFilterRegistration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
// Set the Jersey filter mapping and context path
jerseyFilterRegistration.addUrlPatterns(BASE_PATH + "*");
jerseyFilterRegistration.addInitParameter("jersey.config.servlet.filter.contextPath", BASE_PATH);
// Load the common package and application package
jerseyFilterRegistration.addInitParameter("jersey.config.server.provider.packages", "com.whatever.jaxrs.feature;com.whatever.fig.risk.webservice.resource");
// Enable media type mappings on the URI such as .xml and .json
jerseyFilterRegistration.addInitParameter("jersey.config.server.mediaTypeMappings", "xml:application/xml, json:application/json");
// Enable Java bean validation integration
jerseyFilterRegistration.addInitParameter("jersey.config.beanValidation.enableOutputValidationErrorEntity.servers", "true");
// Disable the application.wadl from being generated and publicly visible (ITSO finding)
jerseyFilterRegistration.addInitParameter("jersey.config.server.wadl.disableWadl", "true");
// Forward 404s to Spring MVC, which serves up the Actuator endpoints and non-jersey resources
jerseyFilterRegistration.addInitParameter("jersey.config.servlet.filter.forwardOn404", "true");
if (isJerseyDebug) {
// Debug parameter switches
jerseyFilterRegistration.addInitParameter("jersey.config.server.monitoring.statistics.enabled", "true");
jerseyFilterRegistration.addInitParameter("jersey.config.server.tracing.type", "ALL");
jerseyFilterRegistration.addInitParameter("jersey.config.server.tracing.threshold", "VERBOSE");
}
return jerseyFilterRegistration;
}
#RequestParam and MultipartFile are both things that are for Spring not Jersey. For Jersey what you want to use is the annotation #FormDataParam and for the parameter, depending on the part type you can have a number of different type of parameters. If the part is a file, you could use an InputStream, File, or byte[] parameter, or if the part is some plain text, you could have String parameter. If you want the filename, you can add a FormDataContentDisposition parameter alongside the part entity parameters. Below is an example
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(
#FormDataParam("file") InputStream file,
#FormDataParam("file") FormDataContentDisposition fdcd,
#FormDataParam("file-detail") String fileDetail) {
}
To make this work, you need to register the MultiPartFeature with your application. You can see this post for ways you can register it.
See also:
File upload along with other object in Jersey restful web service
Jersey documentation for Multipart support

How to get the url of called method resteasy

I making one Rest Service with Restaeasy (java) that have to return the same URL that was called but with one new string
Example Call service:
Post => mybase/myservice/somewrite with some JSON
| Reponse => mybase/myservice/somewrite/123456
So i want to make the mybase/myservice/somewrite url with one generic logic, because if i put String returnURL="mybase/myservice/somewrite"; and i change for example the name of mybase the reponse will not be good
I want somthing like this
someLogicService(JSON);
id=getId();
URL=getContextCallURL();
return URL+\/+id;
But i dont know if this is possible to do it, and less how to do it
You could also inject an instance of type UriInfo using the annotation Context within your resource, as described below:
#Context
private UriInfo uriInfo;
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response makeContact(Contact contact) {
String requestUri = uriInfo.getRequestUri();
(...)
}
Hope it helps you,
Thierry
I found the answer to my problem, i put inject with #context the httpRequest to my function and call absolutPath :
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response makeContact(Contact contact, #Context HttpRequest request) {
return Response.ok().header("location", request.getUri().getAbsolutePath().getPath() + contactService.makeContact(contactJSON)).build();
}

POST doesn't send data

I'm using Angularjs and spring mvc 3. I have in my controller class:
#Controller
#RequestMapping(value = "/elprocesses")
public class ELProcessController {
...
#RequestMapping(value = "/elprocess", method = RequestMethod.POST)
public #ResponseBody void save(#RequestBody final Entity01 entity01,
#RequestBody final Entity02 entity02
) {
...
}
ELProcessController.js :
$scope.saveForm = function(selectedname01) {
$http.post('elprocesses/elprocess', {entity01:selectedname01, entity02:selectedname02});
...
}
it doesn't enter in my spring controller method, but when I send only one data with $http.post('elprocesses/elprocess', selectedname01);
and changing my controller class with:
#RequestMapping(value = "/elprocess", method = RequestMethod.POST)
public #ResponseBody void save(#RequestBody final Entity01 entity01)
this works fine,
What am I doing wrong to send entity01 and entity02?
In your javascript, is selectedname02 defined anywhere?
If it is, then open up your network tab and you'll see whether or not it's sending data. The POST request has the header Content-Type: application/json by default though so make sure you're trying to get json data and not form encoded data or something. I'm not familiar with spring mvc at all so check their docs.

Jaxb in Restful webservices

I have worked on Web services using Jaxb earlier. I geneated Java from xsd, and then I used to post the xml request to the specified URL using HTTP post. Recently I heard about this Restful web services, on reading I felt that what I had been doing earlier is the restful web service only. But, I am not sure about it if its the same thing.
Can anyone explain please.
It sounds like you have been creating the same types of RESTful services. You may be referring to is JAX-RS with is a standard that defines an easier way of creating RESTful services where JAXB is the standard binding layer for the application/xml media type. Below is an example service:
package org.example;
import java.util.List;
import javax.ejb.*;
import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#Stateless
#LocalBean
#Path("/customers")
public class CustomerService {
#PersistenceContext(unitName="CustomerService",
type=PersistenceContextType.TRANSACTION)
EntityManager entityManager;
#POST
#Consumes(MediaType.APPLICATION_XML)
public void create(Customer customer) {
entityManager.persist(customer);
}
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("{id}")
public Customer read(#PathParam("id") long id) {
return entityManager.find(Customer.class, id);
}
#PUT
#Consumes(MediaType.APPLICATION_XML)
public void update(Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("{id}")
public void delete(#PathParam("id") long id) {
Customer customer = read(id);
if(null != customer) {
entityManager.remove(customer);
}
}
}
For More Information
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html
When it comes to say 'RESTful', it's just an convention of HTTP methods and url patterns.
CRUD METHOD URL RESPONSE DESCRIPTION
----------------------------------------------------------------
CREATE POST http://www.doma.in/people 202 Creates a new person with given entity body
READ GET http://www.doma.in/people 200
READ GET http://www.doma.in/people/1 200 404 Reads a single person
UPDATE PUT http://www.doma.in/people/2 204 Updates a single person with given entity body
DELETE DELETE http://www.doma.in/people/1 204 Deletes a person mapped to given id(1)
You can even implement those kind of contracts with Sevlets. Actually I had done with Sevlets before the era of JAX-RS.
And your life will be much more easier when you use JAX-RS.
Here comes a slightly modified version of Mr. Blaise Doughan's.
Nothing's wrong with Mr. Blaise Doughan's code.
I just want to add more for above url patterns.
One of great things that JAX-RS can offer is that you can serve XMLs and JSONs as clients want if you have those fine JAXB classes. See #Producess and #Consumess for those two formats in same method.
When client want to receive as XML with Accept: application/xml, they just get the XML.
When client want to receive as JSON with Accept: application/json, they just get the JSON.
#Path("/customers");
public class CustomersResource {
/**
* Reads all person units.
*/
#POST
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response read() {
final List<Customer> listed = customerBean.list();
final Customers wrapped = Customers.newInstance(listed);
return Response.ok(wrapped).build();
}
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createCustomer(final Customer customer) {
entityManager.persist(customer);
return Response.created("/" + customer.getId()).build();
}
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id: \\d+}")
public Response read(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(customer).build();
}
#PUT
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public void updateCustomer(final Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("/{id: \\d+}")
public void deleteCustomer(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer != null) {
entityManager.remove(customer);
}
return Response.status(Status.NO_CONTENT).build();
}
}
Say you want to serve some images?
#GET
#Path("/{id: \\d+}")
#Produces({"image/png", "image/jpeg"})
public Response readImage(
#HeaderParam("Accept") String accept,
#PathParam("id") final long id,
#QueryParam("width") #DefaultValue("160") final int width,
#QueryParam("height") #DefaultValue("160") final int height) {
// get the image
// resize the image
// make a BufferedImage for accept(MIME type)
// rewrite it to an byte[]
return Response.ok(bytes).build();
// you can event send as a streaming outout
return Response.ok(new StreamingOutput(){...}).build();
}

How do I access the HTTP request?

Say normally I have a REST method in Java
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public String showTime(#FormParam("username") String userName) {
:
:
:
}
which is fine. However, I'm wondering is there a way I can access the full HTTP request with Jersey such as
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public String showTime(#FormParam("username") String userName,#XXXXXX String httpRequest) {
:
:
:
}
where some annotation would give me the full HTTP request to store in a variable. I have tried using #POST but it doesn't seem to work. Any suggestions?
You can use the #Context annotation:
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public String showTime(
#FormParam("username") String userName,
#Context HttpServletRequest httpRequest
) {
// The method body
}
If you want to get the request body, you could use the tip lined out in this post: How to get full REST request body using Jersey?
If you need to know more about the request itself, you could try the #Context annotation as mentioned by sdorra.
I wrote a helper function to address this. Simply extracts request headers and places them in a map.
private Map<String, String> extractHeaders(HttpServletRequest httpServletRequest) {
Map<String, String> map = new HashMap<>();
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
map.put(header, httpServletRequest.getHeader(header));
}
return map;
}