How to create Exception with HttpStatus and send it using Resource of Spring-Hateoas? - spring-boot

I am using Spring-Boot 1.2.7 for developing Spring-Hateoas application with Spring-Data-JPA.
I have developed controller class with methods which returns Resource.
I want to create Exception with HttpStatus and use it in controller class for GET, POST, PUT and DELETE. Please assist me, I am new to this.
Controller Class - ArticleController
#RestController
#RequestMapping(value = "/api/articles")
public class ArticleController {
#Autowired
private ArticleService articleService;
#Autowired
private ArticleRepository articleRepository;
#Autowired
private ArticleResourceAssembler articleResourceAssembler;
/*#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<Resource<Article>> getArticles() {
Collection<Article> articles = articleService.findAll();
List<Resource<Article>> resources = new ArrayList<Resource<Article>>();
for (Article article : articles) {
resources.add(getArticleResource(article));
}
return resources;
}*/
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public PagedResources<Article> getArticles(Pageable pageable, PagedResourcesAssembler assembler) {
Page<Article> articles = articleService.findAll(pageable);
return assembler.toResource(articles, articleResourceAssembler);
}
#RequestMapping(value = "/{article_id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Resource<Article> getArticle(#PathVariable(value = "article_id") long article_id) {
Article article = articleService.findOne(article_id);
if (article == null) {
ResponseEntity.status(HttpStatus.NOT_FOUND);
}
return getArticleResource(article);
}
// Insert Article
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Article> createArtilce(#RequestBody Article article) {
article.setCreated(new Date());
Article savedArticle = articleService.create(article);
article.add(linkTo(methodOn(ArticleController.class).getArticle(savedArticle.getArticle_id()))
.withSelfRel());
// I want to return here HttpStatus.NOT_FOUND
}
private Resource<Article> getArticleResource(Article article) {
Resource<Article> resource = new Resource<Article>(article);
// Link to Article
resource.add(linkTo(methodOn(ArticleController.class).getArticle(article.getArticle_id())).withSelfRel());
return resource;
}
}

You need to implement a class for the exception (that is extends from RuntimeException) and annotated with #ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) or another Status code, and where you want throw that status, you need throw your CustomException.
For Exceptions you didn't write, can use a exception handler method inside your controller like this:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({ExternalException.class})
public void metodoCuandoExcepcionEsLanzada(){
//logging and processing
}

Related

How to invoke #Service from another project with REST call in Spring?

I need to use #Service from another Spring project. I will need a REST call to invoke it but how am I suppose to do it?
This is the service:
#Service
public class LocationsService implements ILocationsService {
private final Logger logger = LoggerFactory.getLogger("LocationsService");
private final ILocationRepository locationRepository;
private final IEvseRepository evseRepository;
private final IConnectorRepository connectorRepository;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public LocationsService(ILocationRepository locationRepository, IEvseRepository evseRepository, IConnectorRepository connectorRepository, EntityManager entityManager) {
this.locationRepository = locationRepository;
this.evseRepository = evseRepository;
this.connectorRepository = connectorRepository;
this.entityManager = entityManager;
}
public Location getLocation(String countryCode, String partyId, String id) {
return locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
}
public Location deleteLocation(String countryCode, String partyId, String id) {
Location location = locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
if (location == null) {
logger.info("Location does not exist.");
return null;
}
locationRepository.deleteById(location.getLocId());
return location;
}
I need to call the service in this controller. The controller is in a different project:
#RestController
#RequestMapping(value = "/locations", produces = MediaType.APPLICATION_JSON_VALUE)
#Api(tags = "Locations management")
public class LocationController {
#Autowired
private LocationsService locationsService;
#RequestMapping(method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Get Locations", notes = "Get locations", nickname = "getLocations",
authorizations = #Authorization(value = "Bearer"))
public ResponseEntity<List<LocationDto>> getLocations() {
List<LocationDto> locations = new ArrayList<>();
return new ResponseEntity<>(locations, HttpStatus.OK);
}
}
I searched for solutions but found nothing helpful and will appreciate any help.
Thank you!

404 Request Resource not found

I am using Spring Framework with restful web services, and I am trying to create an API with restful service and use a get method. I have created a method and I'm trying to have it return a string, but instead I get a 404 error - requested resources not found. Please see my code below:
#RestController
#RequestMapping("/test")
public class AreaController {
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
I am using: localhosr:8080/MyProject/wangdu
This error occurs because you forgot to add
#RequestMapping(value = "/{name}", method = RequestMethod.GET) before your find method:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
Please make sure about this:
The value that the find method is returning is a String with the value "list" and the find method declaration is waiting for a RestResponse object
For example if I have a RestResponse object like this:
public class RestResponse {
private String value;
public RestResponse(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
}
Then try to return the value in this way:
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}
Verify that the method has #RequestMapping annotation with the value that your expect from the url
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
By default the proper way to call the rest resource is by the #RequestMapping value that you set at the #RestController level (#RequestMapping("/test")), in this case could be: http://localhost:8080/test/myValue
If you need to use a different context path then you can change it on the application.properties (for spring boot)
server.contextPath=/MyProject/wangdu
In that case you can call the api like this:
http://localhost:8080/MyProject/wangdu/test/myValue
Here is the complete code for this alternative:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}

Spring MVC Test with RestTemplate: Generic collection fails (even with ParameterizedTypeReference)

I am working with Spring Framework 4.3.1
I have the following domain class
#XmlRootElement(name="persona")
#XmlType(propOrder = {"id","nombre","apellido","fecha"})
public class Persona implements Serializable {
#XmlElement(name="id")
#JsonProperty("id")
public String getId() {
return id;
}
....
Where each getter has the #XmlElement and #JsonProperty annotations.
I am working with JAXB2 and Jackson2
I have the following too:
#XmlRootElement(name="collection")
public class GenericCollection<T> {
private Collection<T> collection;
public GenericCollection(){
}
public GenericCollection(Collection<T> collection){
this.collection = collection;
}
#XmlElement(name="item")
#JsonProperty("collection")
public Collection<T> getCollection() {
return collection;
}
public void setCollection(Collection<T> collection) {
this.collection = collection;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for(Object object : collection){
builder.append("[");
builder.append(object.toString());
builder.append("]");
}
return builder.toString();
}
}
About Testing, the many #Tests methods working through Spring MVC Test work fine. The #Controller and #RestController work how is expected.
Note: I can test the CRUD scenarios, it about the HTTP methods such as POST, PUT, GET and DELETE. Therefore I am able to get one entity and a collection of entities.
Note: from the previous note, all works working around the XML and JSON formats.
Now trying to do testing through the RestTemplate how a kind of programmatic client, it only fails for collections. With the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(httpMessageConverterConfig.marshallingMessageConverter());
converters.add(httpMessageConverterConfig.mappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(converters);
System.out.println("converters.size():" + converters.size());
}
I can confirm converters.size() always prints 2
The following is for XML and JSON
#Test
public void findAllXmlTest(){
RequestEntity<Void> requestEntity = RestControllerSupport_.createRequestEntityForGet(uri, retrieveURI);
ParameterizedTypeReference<GenericCollection<Persona>> parameterizedTypeReference = new ParameterizedTypeReference<GenericCollection<Persona>>(){};
ResponseEntity<GenericCollection<Persona>> responseEntity = restTemplate.exchange(requestEntity, parameterizedTypeReference);
assertThat(responseEntity, notNullValue());
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertThat(responseEntity.getHeaders().getContentType(), is(MediaType.APPLICATION_XML) );
assertThat(responseEntity.getBody(), notNullValue());
assertThat(responseEntity.getBody().getClass(), is(GenericCollection.class));
assertThat(responseEntity.getBody().getCollection(), is(personas));
}
#Test
public void findAllJsonTest(){
RequestEntity<Void> requestEntity = RestControllerSupport_.createRequestEntityForGet(uri, retrieveURI);
ParameterizedTypeReference<GenericCollection<Persona>> parameterizedTypeReference = new ParameterizedTypeReference<GenericCollection<Persona>>(){};
ResponseEntity<GenericCollection<Persona>> responseEntity = restTemplate.exchange(requestEntity, parameterizedTypeReference);
assertThat(responseEntity, notNullValue());
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertThat(responseEntity.getHeaders().getContentType(), is(MediaType.APPLICATION_JSON_UTF8) );
assertThat(responseEntity.getBody(), notNullValue());
assertThat(responseEntity.getBody().getClass(), is(GenericCollection.class));
assertThat(responseEntity.getBody().getCollection(), is(personas));
}
Note: observe I am using ParameterizedTypeReference for both scenarios.
For JSON it works.
But for XML I get:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.manuel.jordan.controller.support.GenericCollection<com.manuel.jordan.domain.Persona>] and content type [application/xml]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
What is wrong or missing?
Your problem that you use MarshallingHttpMessageConverter which isn't GenericHttpMessageConverter, like it is expected for the ParameterizedTypeReference in the HttpMessageConverterExtractor:
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
The MappingJackson2HttpMessageConverter is that one.
So, I suggest you to try with Jaxb2CollectionHttpMessageConverter.

#PathVariable Validation in Spring 4

How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}

Assitance regarding JUnit Testing for Spring Controller Dao

I am new to Junit.Please help me to test Spring hibernate Controller with ContentType is application/json
Below is my Controller
#Controller
#RequestMapping(value="/users")
public class UserServiceImpl implements UserService{
private static Logger logger = Logger.getLogger(UserService.class);
private UserDao userDao;
#Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#RequestMapping(method = RequestMethod.POST,headers = "content-type=application/json")
#ResponseBody
public long addUser(#RequestBody UserForm user) {
logger.info("Creating new user {}"+ user);
return userDao.create(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
public User findUser(#PathVariable(value = "userId") String userId) {
logger.info("Reading user with id {}"+ userId);
User user = userDao.find(userId);
Validate.isTrue(user != null, "Unable to find user with id: " + userId);
return user;
}
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT,headers = "content-type=application/json")
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable(value = "userId") String userId, #RequestBody UserForm user) {
logger.info("Updating user with id {} with {}"+ userId +"->"+ user);
Validate.isTrue(userId.equals(user.getUserId()), "userId doesn't match URL userId: " + user.getUserId());
userDao.update(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void deleteUser(#PathVariable(value = "userId") String userId) {
logger.info("Deleting user with id {}"+ userId);
userDao.delete(userId);
}
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<User> list() {
logger.info("Listing users");
return new ArrayList<User>(userDao.getUsers());
}
}
Can any one Send me the Junit Test case for Any one of the CRUD operations.
Thanks in Advance
Srikanth
If you just want to test your controller, then I would say that mock the DAO. You don't have to care about content types and such because Spring takes care of them. You are interested what the controller method is returning. If you want to test your DAO that User actually is saved to database, that's another story.
But just for testing that controller does what it is supposed to, something like this for example. Example uses EasyMock. I haven't compiled this example so it might have typos.
import static org.easymock.EasyMock.createNiceMock;
public class ControllerTest {
private UserServiceImpl userService;
private UserDao userDaoMock;
#Before
public void setup() {
userDaoMock = createNiceMock(UserDao.class);
userService = new UserServiceImpl();
userSerivce.setUserDao(userDaoMock);
}
#Test
public void testAddUser() {
UserForm userForm = new UserForm();
long expectedResult = 5L;
expect(userDaoMock.create(userForm)).andReturn(expectedResult);
replay(userDaoMock);
long actualResult = userService.addUser(userForm);
verify(userDaoMock);
assertEquals(expectedResult, actualResult);
}
}

Resources