I'd like to write a rest service that accepts and produces XML or JSON based on the information from headers. To do so, I followed one of tutorials. The problem is that when I try to read fields of dto in Spring controller, they are all set to null.
For test purposes, I send in body a DTO and in controller I return it concatenating string Changed to two of its fields.
In body I send:
<?xml version="1.0" encoding="UTF-8"?>
<name>name</name>
<description>description</description>
However, I receive:
<Dto name="null Changed" description="null Changed"/>
I send request by Postman:
Here's my configuration:
Controller
#RestController
public class Controller {
#RequestMapping(value = "/endpoint", method = RequestMethod.POST,
consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE},
produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public Dto getAndReturnEntity(#RequestBody Dto dto) {
dto.setName(dto.getName() + " Changed");
dto.setDescription(dto.getDescription() + " Changed");
return dto;
}
}
DTO
#JacksonXmlRootElement
public class Dto {
#JacksonXmlProperty(isAttribute = true) // I also tried without it
private String name;
#JacksonXmlProperty(isAttribute = true)
private String description;
// getters and setters ommited for brevity
}
Configuration for Spring
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).
favorParameter(false).
parameterName("mediaType").
ignoreAcceptHeader(false).
useJaf(false).
defaultContentType(MediaType.APPLICATION_JSON).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
}
Relevant part of pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
The input XML should be like this:
<Dto>
<name>abcd</name>
<description>desc</description>
</Dto>
In case you want the tag names be different than you can use either custom object mapper or add #JsonProperty.
Related
I am following spring data rest from https://spring.io/guides/gs/accessing-data-rest/ and I am only using
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
I would like to know how can I return all records (without pagination) but not using spring-boot-starter-web.I wants to keep my code as small as possible.
I tried following but it is not working
#RepositoryRestResource(collectionResourceRel = "people" , path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findAllByLastName(#Param("name") String name);
default List<Person> findAll(){
Pageable pageable = null;
return (List<Person>) this.findAll(pageable);
};
}
I mean if I have whole MVC, I can do it but I like to keep my code to minimum.
Spring Data REST is itself a Spring MVC application and is designed in
such a way that it should integrate with your existing Spring MVC
applications with little effort. An existing (or future) layer of
services can run alongside Spring Data REST with only minor additional
work.
If you are using current version of spring boot, there is no need to mark your repository with #RepositoryRestResource; also spring will auto-configure Spring Data Rest once it found the spring-data-rest dependency in your path, bellow you will find steps with minimum config :
In pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
Define your Entity + Repository :
Order.java
#Entity(name = "SampleOrder")
#Data
public class Order {
#Id #GeneratedValue//
private Long id;
private String name;
}
OrderRepository.java
public interface OrderRepository extends CrudRepository<Order, Long> {
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Test your API :
curl http://localhost:8080
< HTTP/1.1 200 OK
< Content-Type: application/hal+json
{ "_links" : {
"orders" : {
"href" : "http://localhost:8080/orders"
}
}
}
As #abdelghani-roussi shows, you can use the CrudRepository instead of the PagingAndSortingRepository, e.g.:
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findAllByLastName(#Param("name") String name);
// don't need to define findAll(), it's defined by CrudRepository
}
and then the default findAll() method will return a List<Person> that isn't paged.
Note: as I mentioned in my comment, by including the dependency on spring-boot-starter-data-rest you are also pulling in the Web dependencies, so you can't avoid that.
I am using Hibernate validation in Spring Boot project. Though I am able to read messages from ValidationMessage.properties file and also arguments in curly braces as {min}, {max}:
Size.accountRequestBean.firmName=size should be between {min} and {max}
ValidationMessages.properties
Size.accountRequestBean.firmName={0} should be between {1} and {2}
But when i use Size.accountRequestBean.firmName={0} should be between {1} and {2} the message displayed is {0} should be between {1} and {2}:
expected :size should be between 2 and 30.
DemoApplication.java
#SpringBootApplication
public class DemoApplication extends WebMvcConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Bean.java
public class Bean {
#Size(min=2, max=30,message="{Size.accountRequestBean.firmName}")
private String firmName;
}
Validation dependencies I am Using:
<!-- Hibernate Validator -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
So please suggest where I am doing wrong?
Rename your ValidationMessage.properties file to messages.properties (by default Spring expects to find it under the resources folder) and put the following properties inside it:
bean.hasErrors=Please fix the following errors
bean.firmName=Firm Name
Size.bean.firmName={0} should be between {2} and {1}
Note the order of precedence here is: code.objectName.fieldName. And objectName must be the same as your class name, so instead accountRequestBean put just bean (as your object Bean.java).
Then, you can omit the message key in #Size annotation. When you use messages.properties file Spring Boot automatically get its messages:
public class Bean {
#Size(min = 2, max = 30)
private String firmName;
// getter, setter
}
Make sure you use #Valid annotation in your controller to use Bean validation, for example:
#RequestMapping(value = "/doBean", method = RequestMethod.POST)
public String doBean (#Valid Bean checkoutCommand, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "success";
}
Please look for the complete code example.
You should get something like this:
On my project the problem was in this setting:
var messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setUseCodeAsDefaultMessage(true);
after
setUseCodeAsDefaultMessage(false)
all worked
I have a user list class and an API that returns user list and total records.
The class is as follows :
#JsonInclude(JsonInclude.Include.NON_NULL)
public class FMSResponseInfo {
#JsonProperty("status")
private String status;
#JsonProperty("message")
private String message;
#JsonProperty("data")
private Object data;
#JsonProperty("status")
public String getStatus() {
return status;
}
#JsonProperty("status")
public void setStatus(String status) {
this.status = status;
}
#JsonProperty("message")
public String getMessage() {
return message;
}
#JsonProperty("message")
public void setMessage(String message) {
this.message = message;
}
#JsonProperty("data")
public Object getData() {
return data;
}
#JsonProperty("data")
public void setData(Object data) {
this.data = data;
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UserListResDTO {
#JsonProperty("users")
private List<UserDTO> users;
#JsonProperty("totalRecords")
private long totalRecords;
public List<UserDTO> getUsers() {
return users;
}
public void setUsers(List<UserDTO> users) {
this.users = users;
}
public long getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(long totalRecords) {
this.totalRecords = totalRecords;
}
}
I am setting an object of type UserListResDTO in FMSResponseInfo as shown below.
I have been successful in creating web services and returning response as json, so far. But the problem I am facing is that the API returns the response as follows :
{"data":"org.package.UserListResDTO#70783307","message":"Success","status":"200"}
And this is how I have written the web service :
#Path("/getUsers")
#GET
#Produces(MediaType.APPLICATION_JSON)
public FMSResponseInfo getUsers(#QueryParam("page") #DefaultValue("0") int page) {
System.out.println("In getUsers()");
FMSResponseInfo fmsResponseInfo = new FMSResponseInfo();
try {
UserListResDTO userList = fmsUserManager.getAllUsers(page);
fmsResponseInfo.setData(userList);
fmsResponseInfo.setStatus(FMSErrorMessageEnum.SUCCESS_CODE.getValue());
fmsResponseInfo.setMessage(FMSErrorMessageEnum.SUCCESS_MESSAGE.getValue());
} catch (Exception e) {
return FMSUtil.getErrorResponseInfo(FMSErrorMessageEnum.INTERNAL_SERVER_ERROR_CODE.getValue(),
e.getMessage());
}
System.out.println("Out getUsers()");
return fmsResponseInfo;
}
I guess there is some problem with the dependencies or something that I am unable to resolve. Major dependencies in my pom are :
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
and I am creating Web services by extending Application class as follow :
#ApplicationPath("rest")
public class FMSApplication extends Application {
public Set<Class<?>> getClasses(){
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(FMSUserManagerWebService.class);
set.add(FMSDocumentManagerWebService.class);
set.add(FMSInboxManagerWebService.class);
set.add(FMSLocationManagerWebService.class);
return set;
}
}
Any help will be really appreciated as I am new to this REST web services and have been stuck for quite long.
This link will explain the answer
https://jersey.java.net/documentation/latest/media.html#d0e7963
9.1.1.1. POJO support
POJO support represents the easiest way to convert your Java Objects
to JSON and back.
Media modules that support this approach are MOXy and Jackson
The link to Jackson includes
9.1.4.1. Dependency
To use Jackson 2.x as your JSON provider you need to add
jersey-media-json-jackson module to your pom.xml file
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.2</version>
</dependency>
I don't know much about moxy but you had jackson on your CLASSPATH and were using Jackson annonations. Jersey however was configured to use moxy.
From link
JSON binding support via MOXy is a default and preferred way of
supporting JSON binding in your Jersey applications since Jersey 2.0.
When JSON MOXy module is on the class-path, Jersey will automatically
discover the module and seamlessly enable JSON binding support via
MOXy in your applications.
MOXy seemed to have handled FMSResponseInfo. Why it didn't handle the other object I do not know. But since you wanted to use Jackson you should have been using the Jackson module.
As suggested by Shire Resident in the comments using the following dependency I was able to resolve the problem :
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.2</version>
</dependency>
I use guice, jetty, jersey+jackson in my stack to run a restful app. It works perfectly.
Then, I tried to add Jersey's Bean validation but I got no errors, no warnings... and no validation. I've read many articles, but non of these helped me out.
Here is my JerseyConfigModule:
public class JerseyConfigModule extends ServletModule {
#Override
protected void configureServlets() {
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
bind(GuiceContainer.class);
Set<Class<?>> classes=new ResourceConfig().property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE,true).getClasses();
for (Class<?> aClass : classes) {
bind(aClass);
}
serve("/rest/*").with(GuiceContainer.class, initParams);
}
}
My Jersey resource:
#Path("/user")
public class UserResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response post(#Valid StoreUserDTO user){
}
}
In an another Guice module I bind this resource:
bind(UserResource.class);
The Bean used in parameter:
public class StoreUserDTO {
#NotNull
private String name;
#NotNull
private String email;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
I use jersey-guice and glassfish's jersey-bean-validation (and I want this):
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-guice</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.22.1</version>
</dependency>
What did I forget? Lots of examples I've found is not worked or wasn't for Jersey 2, just for 1.
Yes, I know Jersey supports the bean validation officially, but in the offical docs, I didn't find any info about how to integrate with Guice.
I have problem with Ajax request on form submit. The form contains these stringify JSON data:
{"articleContent":"<p>aaa</p>","title":"Po vyplnění titulku aktuality budete","header":"aa","enabled":false,"timestamp":"1358610697521","publishedSince":"03.01.2013 00:00","publishedUntil":"","id":"10"}
When json contains "03.01.2013 00:00" value, server respons is 400 Bad Request
Problem is that, custom DateTimePropertyEditor (which is registrated with #InitBinder) is not called, and DateTime in String format is not conveted. Have you any idea How to solve this problem?
Controllers mapped method, which is processing request
#RequestMapping( value = "/admin/article/edit/{articleId}", method = RequestMethod.POST, headers = {"content-type=application/json"})
public #ResponseBody JsonResponse processAjaxUpdate(#RequestBody Article article, #PathVariable Long articleId){
JsonResponse response = new JsonResponse();
Article persistedArticle = articleService.getArticleById(articleId);
if(persistedArticle == null){
return response;
}
List<String> errors = articleValidator.validate(article, persistedArticle);
if(errors.size() == 0){
updateArticle(article, persistedArticle);
response.setStatus(JsonStatus.SUCCESS);
response.setResult(persistedArticle.getChanged().getMillis());
}else{
response.setResult(errors);
}
return response;
}
InitBinder
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(DateTime.class, this.dateTimeEditor);
}
I solved this problem with using #JsonDeserialize
#JsonDeserialize(using=DateTimeDeserializer.class)
public DateTime getPublishedUntil() {
return publishedUntil;
}
I have to implemetd custom Deserializer.
public class DateTimeDeserializer extends StdDeserializer<DateTime> {
private DateTimeFormatter formatter = DateTimeFormat.forPattern(Constants.DATE_TIME_FORMAT);
public DateTimeDeserializer(){
super(DateTime.class);
}
#Override
public DateTime deserialize(JsonParser json, DeserializationContext context) throws IOException, JsonProcessingException {
try {
if(StringUtils.isBlank(json.getText())){
return null;
}
return formatter.parseDateTime(json.getText());
} catch (ParseException e) {
return null;
}
}
}
This is not handled by a Property Editor - which acts on form fields and not on json bodies. To handle a non-standard date format in a json, you will have to customize the underlying ObjectMapper. Assuming you are using jackson 2.0+, these are what you can do:
a. Tag the publishedSince field with an annotation that tells Object mapper the format for date - based on instructions here:
public class Article{
...
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM.dd.yyyy HH:mm")
private Date publishedSince;
}
b. Or second option is to modify the ObjectMapper itself, this could be global though, so may not work for you:
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper(){
super.setDateFormat(new SimpleDateFormat("MM.dd.yyyy hh:mm"));
}
}
and configure this with Spring MVC:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="..CustomObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
With Spring MVC 4.2.1.RELEASE, you need to use the new Jackson2 dependencies as below for the Deserializer to work.
Dont use this
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
Use this instead.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.2</version>
</dependency>
Also use com.fasterxml.jackson.databind.JsonDeserializer and com.fasterxml.jackson.databind.annotation.JsonDeserialize for the deserialization and not the classes from org.codehaus.jackson