Hot questions for Spring RestController

Top 10 Java Open Source / Spring / Spring RestController

Question:

I am trying to read HTTP headers in Spring based REST API. I followed this. But I am getting this error:

No message body reader has been found for class java.lang.String, ContentType: application/octet-stream

I am new to Java and Spring so can't figure this out.

This is how my call looks like:

@WebService(serviceName = "common")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public interface CommonApiService {

    @GET
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/data")
    public ResponseEntity<Data> getData(@RequestHeader(value="User-Agent") String userAgent, @DefaultValue ("") @QueryParam("ID") String id);
}

I have tried @Context: HTTPHeader is null in this case.

How to get values from HTTP headers?


Answer:

The error that you get does not seem to be related to the RequestHeader.

And you seem to be confusing Spring REST services with JAX-RS, your method signature should be something like:

@RequestMapping(produces = "application/json", method = RequestMethod.GET, value = "data")
@ResponseBody
public ResponseEntity<Data> getData(@RequestHeader(value="User-Agent") String userAgent, @RequestParam(value = "ID", defaultValue = "") String id) {
    // your code goes here
}

And your REST class should have annotations like:

@Controller
@RequestMapping("/rest/")

Regarding the actual question, another way to get HTTP headers is to insert the HttpServletRequest into your method and then get the desired header from there.

Example:

@RequestMapping(produces = "application/json", method = RequestMethod.GET, value = "data")
@ResponseBody
public ResponseEntity<Data> getData(HttpServletRequest request, @RequestParam(value = "ID", defaultValue = "") String id) {
    String userAgent = request.getHeader("user-agent");
}

Don't worry about the injection of the HttpServletRequest because Spring does that magic for you ;)

Question:

This returns 200 OK with Content-Length: 0

@RestController
public class RepoController {
    @RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
    public Object getDocument(@PathVariable long id) {
       return null;
    }

}

Simply put I'd like it to return 204 No Content on null.

Is there a way to force spring-mvc/rest to return 204 on null not 200? I dont want to change every rest method to return ResponseEntity or something like that, only map null to 204


Answer:

You can use the @ResponseStatus annotation. This way you can have a void method and you don't have to build a ResponseEntity.

@DeleteMapping(value = HERO_MAPPING)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long heroId) {
    heroService.delete(heroId);
}

BTW returning 200 when the object exists and 204 otherwise it's a bit unusual regarding API REST design. It's common to return a 404 (not found) when the requested object is not found. And this can be achieved using an ControllerAdvice.

In Spring REST it's better to handle Exceptions with a Exception handler instead of putting logic to decide the response status, etc. This is an example using the @ControllerAdvice annotation: http://www.jcombat.com/spring/exception-handling-in-spring-restful-web-service

Question:

I'm using swagger2 in my spring boot project. It's working well, but I need to exclude the basic-error-controller from the api. Currently I'm using the following code using regex. It's working but is there any perfect way to do this.

CODE :

@Bean
public Docket demoApi() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.regex('(?!/error.*).*'))
            .build()
}

Answer:

After searching in google I got the solution from one issue in GitHub, [question] How to exclude the basic-error-controller from being added to the swagger description?. It can be done using Predicates.not().

Code looks like as follows after using Predicates.not().

@Bean
public Docket demoApi() {
    return new Docket(DocumentationType.SWAGGER_2)//<3>
            .select()//<4>
            .apis(RequestHandlerSelectors.any())//<5>
            .paths(Predicates.not(PathSelectors.regex("/error.*")))//<6>, regex must be in double quotes.
            .build()
}

Question:

I want to add an upload function to my spring boot application; this is my upload Rest Controller

package org.sid.web;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.sid.entities.FileInfo;

@RestController
public class UploadController {
  @Autowired
  ServletContext context;

  @RequestMapping(value = "/fileupload/file", headers = ("content-type=multipart/*"), method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public ResponseEntity<FileInfo> upload(@RequestParam("file") MultipartFile inputFile) {
    FileInfo fileInfo = new FileInfo();
    HttpHeaders headers = new HttpHeaders();
    if (!inputFile.isEmpty()) {
      try {
        String originalFilename = inputFile.getOriginalFilename();
        File destinationFile = new File(
            context.getRealPath("C:/Users/kamel/workspace/credit_app/uploaded") + File.separator + originalFilename);
        inputFile.transferTo(destinationFile);
        fileInfo.setFileName(destinationFile.getPath());
        fileInfo.setFileSize(inputFile.getSize());
        headers.add("File Uploaded Successfully - ", originalFilename);
        return new ResponseEntity<FileInfo>(fileInfo, headers, HttpStatus.OK);
      } catch (Exception e) {
        return new ResponseEntity<FileInfo>(HttpStatus.BAD_REQUEST);
      }
    } else {
      return new ResponseEntity<FileInfo>(HttpStatus.BAD_REQUEST);
    }
  }
}

but when testing this in postman with inserting http://localhost:8082/fileupload/file and adding a file to the body i got this error: "exception": "org.springframework.web.multipart.support.MissingServletRequestPartException", "message": "Required request part 'file' is not present",


Answer:

This is how your request in Postman should look like:

My sample code:

application.properties

#max file and request size 
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=11MB

Main Application Class:

Application.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Rest controller class:

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;


    @Controller
    @RequestMapping("/fileupload")
    public class MyRestController {

    @RequestMapping(value = "/file", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
        public @ResponseBody String myService(@RequestParam("file") MultipartFile file,
                @RequestParam("id") String id) throws Exception {

    if (!file.isEmpty()) { 

           //your logic
                        }
return "some json";

                }
    }

pom.xml

//...

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

....



<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>

//...

Question:

I have created a simple REST service (POST). But when i call this service from postman @RequestBody is not receiving any values.

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
public class Add_Policy {
    @ResponseBody
    @RequestMapping(value = "/Add_Policy", headers = {
            "content-type=application/json" }, consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
    public Policy GetIPCountry( @RequestBody Policy policy) {

        System.out.println("Check value: " + policy.getPolicyNumber());
        return policy;

    }


}

My java Bean object is like below:

public class Policy {
    private String PolicyNumber;
    private String Type;
    private String Tenture;
    private String SDate;
    private String HName;
    private String Age;

    public String getPolicyNumber() {
        return PolicyNumber;
    }

    public void setPolicyNumber(String policyNumber) {
        PolicyNumber = policyNumber;
    }

    public String getType() {
        return Type;
    }

    public void setType(String type) {
        Type = type;
    }

    public String getTenture() {
        return Tenture;
    }

System.out.println is printing a null as a value for PolicyNumber.

Please help me to resolve this issue.

JSON which i am passing in request body is

{
    "PolicyNumber": "123",
    "Type": "Test",
    "Tenture": "10",
    "SDate": "10-July-2016",
    "HName": "Test User",
    "Age": "10"
}

I have even set Content-Type to application/json in postman


Answer:

Try setting the first character of the properties in your JSON to lower case. Eg.

{
    "policyNumber": "123",
    "type": "Test",
    "tenture": "10",
    "sDate": "10-July-2016",
    "hName": "Test User",
    "age": "10"
}

Basically, Spring uses getter and setter to set the properties of the the bean object. And it takes the property of the JSON object, matches it with the setter of the same name. eg to set the policyNumber property it tries to find a setter with the name setpolicyNumber() in your bean class and use that to set the value of your bean object.

Question:

What is the typical use case code that shows the difference between those two annotations - meaning the @RestController and the @RepositoryRestController - ?


Answer:

According to the annotation the RepositoryRestController is a way to provide custom controllers that still take advantage of spring data rest functionality

http://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.overriding-sdr-response-handlers

Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the @RepositoryRestController annotation instead of a standard Spring MVC @Controller or @RestController.

Most importantly the RepositoryRestController is aware of the spring data rest base path and will be served under this base path.

Question:

I've been going through my head the best way to design a JSON API using Spring MVC. As we all know IO is expensive, and thus I don't want to make the client make several API calls to get what they need. However at the same time I don't necessarily want to return the kitchen sink.

As an example I was working on a game API similar to IMDB but for video games instead.

If I returned everything connected to Game it would look something like this.

/api/game/1

{
    "id": 1,
    "title": "Call of Duty Advanced Warfare",
    "release_date": "2014-11-24",
    "publishers": [
        {
            "id": 1,
            "name": "Activision"
        }
    ],
    "developers": [
        {
            "id": 1,
            "name": "Sledge Hammer"
        }
    ],
    "platforms": [
        {
            "id": 1,
            "name": "Xbox One",
            "manufactorer": "Microsoft",
            "release_date": "2013-11-11"
        },
        {
            "id": 2,
            "name": "Playstation 4",
            "manufactorer": "Sony",
            "release_date": "2013-11-18"
        },
        {
            "id": 3,
            "name": "Xbox 360",
            "manufactorer": "Microsoft",
            "release_date": "2005-11-12"
        }
    ],
    "esrbRating": {
        "id": 1,
        "code": "T",
        "name": "Teen",
        "description": "Content is generally suitable for ages 13 and up. May contain violence, suggestive themes, crude humor, minimal blood, simulated gambling and/or infrequent use of strong language."
    },
    "reviews": [
        {
            "id": 1,
            "user_id": 111,
            "rating": 4.5,
            "description": "This game is awesome"
        }
    ]
}

However they may not need all this information, but then again they might. Making calls for everything seems like a bad idea from I/O and performance.

I thought about doing it by specifying include parameter in the requests.

Now for example if you did not specify any includes all you would get back is the following.

{
    "id": 1,
    "title": "Call of Duty Advanced Warfare",
    "release_date": "2014-11-24"
}

However it you want all the information your requests would look something like this.

/api/game/1?include=publishers,developers,platforms,reviews,esrbRating

This way the client has the ability to specify how much information they want. However I'm kind of at a loss the best way to implement this using Spring MVC.

I'm thinking the controller would look something like this.

public @ResponseBody Game getGame(@PathVariable("id") long id, 
    @RequestParam(value = "include", required = false) String include)) {

        // check which include params are present

        // then someone do the filtering?
}

I'm not sure how you would optionally serialize the Game object. Is this even possible. What is the best way to approach this in Spring MVC?

FYI, I am using Spring Boot which includes Jackson for serialization.


Answer:

Instead of returning a Game object, you could serialize it as as a Map<String, Object>, where the map keys represent the attribute names. So you can add the values to your map based on the include parameter.

@ResponseBody
public Map<String, Object> getGame(@PathVariable("id") long id, String include) {

    Game game = service.loadGame(id);
    // check the `include` parameter and create a map containing only the required attributes
    Map<String, Object> gameMap = service.convertGameToMap(game, include);

    return gameMap;

}

As an example, if you have a Map<String, Object> like this:

gameMap.put("id", game.getId());
gameMap.put("title", game.getTitle());
gameMap.put("publishers", game.getPublishers());

It would be serialized like this:

{
  "id": 1,
  "title": "Call of Duty Advanced Warfare",
  "publishers": [
    {
        "id": 1,
        "name": "Activision"
    }
  ]
}