Spring rest controller not working for file upload - java

I have tried a rest api without any View with mongodb. My application is working fine with Spring boot embedded server with no error. With standalone tomcat application is running without error but cannot access #RestController mapped url. It shows 404 page not found. It is not creating that url but other url works fine.
Here is config
FileStorageProperties.java
#Component
public class FileStorageProperties {
private static String baseDir = "E://temp//FileManager//";
private String uploadDir = "";
public String getUploadDir() {
return baseDir + uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = baseDir + uploadDir;
}
}
MediaTypeUtils.java
public class MediaTypeUtils {
// abc.zip
// abc.pdf,..
public static MediaType getMediaTypeForFileName(ServletContext servletContext, String fileName) {
// application/pdf
// application/xml
// image/gif, ...
String mineType = servletContext.getMimeType(fileName);
try {
MediaType mediaType = MediaType.parseMediaType(mineType);
return mediaType;
} catch (Exception e) {
return MediaType.APPLICATION_OCTET_STREAM;
}
}
}
Here is model
Avatar.java:
#Document
#Component
public class Avatar {
#Id
// #Indexed(unique = true)
private String id;
private String originalName;
#Indexed(unique = true)
private String generatedName;
private String size;
#DBRef
private User user;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getOriginalName() {
return originalName;
}
public void setOriginalName(String originalName) {
this.originalName = originalName;
}
public String getGeneratedName() {
return generatedName;
}
public void setGeneratedName(String generatedName) {
this.generatedName = generatedName;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public String toString() {
return "Avatar [id=" + id + ", originalName=" + originalName + ", generatedName=" + generatedName + ", size="
+ size + ", user=" + user + "]";
}
}
UploadFileResponse.java
public class UploadFileResponse {
private String fileName;
private String fileType;
private long size;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public UploadFileResponse(String fileName, String fileType, long size) {
this.fileName = fileName;
this.fileType = fileType;
this.size = size;
}
}
FileStorageException.java
public class FileStorageException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -2482390550398268830L;
public FileStorageException(String message) {
super(message);
}
public FileStorageException(String message, Throwable cause) {
super(message, cause);
}
}
MyFileNotFoundException.java
#ResponseStatus(HttpStatus.NOT_FOUND)
public class MyFileNotFoundException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -7680896574598153059L;
public MyFileNotFoundException(String message) {
super(message);
}
public MyFileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
Now repository:
AvatarRepository.java
public interface AvatarRepository extends MongoRepository<Avatar, String>{
public Avatar findAvatarByGeneratedName(String generatedName);
public Avatar findAvatarByUser(User user);
}
AvatarRepositopryImpl.java - There are some overrided MongoRepository methods which are ignored on the following code as those are not used at all.
#Repository
public class AvatarRepositopryImpl implements AvatarRepository {
#Autowired
MongoTemplate mongoTemplate;
private static final String COLLECTION_NAME = "avatar";
#Override
public <S extends Avatar> List<S> saveAll(Iterable<S> entities) {
// TODO Auto-generated method stub
return null;
}
#Override
public List<Avatar> findAll() {
return mongoTemplate.findAll(Avatar.class, COLLECTION_NAME);
}
#Override
public <S extends Avatar> S save(S entity) {
return mongoTemplate.save(entity);
}
#Override
public void deleteById(String id) {
mongoTemplate.remove(id);
}
#Override
public void delete(Avatar entity) {
mongoTemplate.remove(entity);
}
#Override
public Avatar findAvatarByGeneratedName(String generatedName) {
Query query = new Query();
query.addCriteria(Criteria.where("generatedName").is(generatedName));
return (Avatar) mongoTemplate.findOne(query, Avatar.class);
}
#Override
public Avatar findAvatarByUser(User user) {
// System.out.println(user);
Query query = new Query();
query.addCriteria(Criteria.where("user").is(user));
return (Avatar) mongoTemplate.find(query, Avatar.class);
}
}
Here is my service:
AvatarService.java
#Service
public class AvatarService {
#Autowired
private Avatar avatar;
//
// #Autowired
// private FileStorageService fileStorageService;
#Autowired
AvatarRepository avatarRepository;
/**
* #param fileStorageService
* #param avatarRepository
*/
public Avatar findAvatarByGeneratedName(String generatedName) {
return avatarRepository.findAvatarByGeneratedName(generatedName);
}
public Avatar findAvatarByUser(User user) {
System.out.println(user);
return avatarRepository.findAvatarByUser(user);
}
public Avatar saveAvatar(User user,String fileOriginalName, String fileNameToSave, String fileSize) {
avatar.setId(UUID.randomUUID().toString());
avatar.setOriginalName(fileOriginalName);
avatar.setGeneratedName(fileNameToSave);
avatar.setSize(fileSize);
avatar.setUser(user);
return avatarRepository.save(avatar);
}
public void deleteAvatarByGeneratedName(String generatedName, Avatar avatar) {
avatar = findAvatarByGeneratedName(generatedName);
avatarRepository.deleteById(avatar.getId());
}
AvatarManager.java
public class AvatarManager {
AvatarService avatarService;
#Autowired
Avatar avatar;
public AvatarManager(AvatarService avatarService) {
this.avatarService = avatarService;
}
public String saveAvatar(User user, MultipartFile file) {
deleteAvatarByUser(user);
String[] ext = file.getOriginalFilename().split(Pattern.quote("."));
String generatedName = new UUIDGenerator().generateId(avatar).toString() + "." + ext[ext.length - 1];
avatarService.saveAvatar(user, file.getOriginalFilename(), generatedName, file.getSize() + "");
new FileStorageService(new FileStorageProperties()).uploadFile(file, generatedName);
new UploadFileResponse(generatedName, file.getContentType(), file.getSize());
return generatedName;
}
public void deleteAvatar(String generatedName) {
Avatar avatar = avatarService.findAvatarByGeneratedName(generatedName);
if (avatar != null) {
new FileStorageService(new FileStorageProperties()).deleteFile(generatedName);
avatarService.deleteAvatarByGeneratedName(generatedName, avatar);
}
}
public void deleteAvatarByUser(User user) {
// System.out.println(user);
avatar = avatarService.findAvatarByUser(user);
// System.out.println(avatar.getGeneratedName());
if (avatar != null) {
deleteAvatar(avatar.getGeneratedName());
}
}
public ResponseEntity<Resource> downloadAvatar(String avatarGeneratedName, HttpServletRequest request) {
// System.out.println(request);
return new FileStorageService(new FileStorageProperties()).downloadFile(avatarGeneratedName, request);
}
}
Now Controller. I am giving two mapping. The first one is not involved with file manager. It works on both server. The another one is working on spring boot embedded server but not on standalone tomcat. It also is not showing any error or create any url.
Here is the Controller.java;
#CrossOrigin(origins = { "*" })
#RestController
public class UserController {
#Autowired
EmployeeService employeeService;
#Autowired
UserService userService;
#Autowired
AvatarService avatarService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
//It works on both server
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginEmployee(#RequestParam Map<String, String> data) {
return new EmployeeManager(employeeService, userService)
.validateUserLogin(data.get("0").toString(), data.get("1"), passwordEncoder).toString();
}
//It doesn't work with standalone server
#RequestMapping(value = "/edit-employee-profile", method = RequestMethod.POST)
public void editProfileEmployee(#RequestParam(value = "avatar", required = false) MultipartFile file,
#RequestParam(value = "user") String data) {
JSONParser jp = new JSONParser(data);
System.out.println(jp);
LinkedHashMap<String, Object> userInfo;
try {
userInfo = jp.parseObject();
User user = userService.findUserByEmail(userInfo.get("email").toString());
System.out.println(user);
user.setAvatarExistance(true);
userService.updateUser(user);
// Employee employee = employeeService.findEmployeeByUser(user);
System.out.println(data);
new EmployeeManager(employeeService, userService).employeeProfileEdit(data);
if (file != null) {
System.out.println(file);
new AvatarManager(avatarService).saveAvatar(user, file);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return "";
}

Post the file as request body (rather than as URL parameter and #RequestParam) to not excced the safe maximum URL length:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
public void uploadFile(ModelMap m, MultipartHttpServletRequest request) {
MultiValueMap<String, MultipartFile> multiFileMap = request.getMultiFileMap();
List<MultipartFile> files = multiFileMap.isEmpty() ? null : multiFileMap.values().iterator().next();
MultipartFile file = files != null && !files.isEmpty() ? files.get(0) : null;
// ...
}

Related

thenReturn() wants Optional, but test fails

I'm trying to do an Unit test, but I receive the error 'cannot resolve the method thenReturn(suspectTest) ; wrap using java.Optional', but when I do (.thenReturn(Optional.of(suspectTest)), the test fails - 'Optional cannot be cast to class Suspect'. Here it is the test, the SuspectService and the SuspectController (edited post*). Thank you in advance!
#DataJpaTest
public class ControllerTest {
#Mock
Model model;
#Mock
SuspectService suspectService;
SuspectController suspectController;
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Before
public void setUp() throws Exception {
suspectController = new SuspectController(suspectService);
}
#Captor
ArgumentCaptor<Suspect> argumentCaptor;
#Test
public void showById() {
Integer id = 1;
Suspect suspectTest = new Suspect();
suspectTest.setId(Long.valueOf(id));
when(suspectService.getById(Long.valueOf(id))).thenReturn(Optional.of(suspectTest));
String viewName = suspectController.showById(id.toString(), model);
Assert.assertEquals("info", viewName);
verify(suspectService, times(1)).getById(Long.valueOf(id));
verify(model, times(1))
.addAttribute(eq("suspect"), argumentCaptor.capture() );
Suspect suspectArg = argumentCaptor.getValue();
Assert.assertEquals(suspectArg.getId(), suspectTest.getId() );
}
}
#Service
public class SuspectService implements BaseService<Suspect> {
private final com.unibuc.AWBD_Project_v1.repositories.SuspectRepository suspectRepository;
#Autowired
public SuspectService(SuspectRepository SuspectRepository) {
this.suspectRepository = SuspectRepository;
}
#Override
public Suspect insert(Suspect object) {
return suspectRepository.save(object);
}
#Override
public Suspect update(Long id, Suspect updatedObject) {
var foundId = suspectRepository.findById(id);
return foundId.map(suspectRepository::save).orElse(null);
}
#Override
public List<Suspect> getAll() {
return suspectRepository.findAll();
}
#Override
public Optional<Suspect> getById(Long id) {
return suspectRepository.findById(id);
}
#Override
public void deleteById(Long id)
{
try {
suspectRepository.deleteById(id);
} catch (SuspectException e) {
throw new SuspectException("Suspect not found");
}
}
#Override
public Page<Suspect> findAll(int page, int size, String sortBy, String sortType){
Sort sort = sortType.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending() :
Sort.by(sortBy).descending();
Pageable pageable = PageRequest.of(page - 1, size, sort);
return suspectRepository.findAll(pageable);
}
}
#RestController
#RequestMapping("/api/suspect")
public class SuspectController implements BaseController<Suspect> {
private final com.unibuc.AWBD_Project_v1.services.SuspectService SuspectService;
#Autowired
public SuspectController(SuspectService SuspectService) {
this.SuspectService = SuspectService;
}
#PostMapping("/suspects/add")
#Override
public String NewObject(Model model) {
model.addAttribute("suspect", new Suspect());
return "addsuspect";
}
#PostMapping("/suspects/update/{id}")
#Override
public String update(#PathVariable Long id, #Valid #ModelAttribute("suspect") Suspect suspect, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors())
{
boolean update = true;
model.addAttribute("update", update);
return "addsuspect";
}
SuspectService.insert(suspect);
return "redirect:/suspects";
}
#DeleteMapping("/suspects/delete/{id}")
#Override
public String deleteById(#PathVariable Long id) {
try {
SuspectService.deleteById(id);
} catch (SuspectException e)
{
throw new SuspectException("Suspect with " + id + " does not exist!");
}
return "redirect:/suspects";
}
#GetMapping("/suspect/{id}")
public String showById(#PathVariable String id, Model model){
model.addAttribute("suspect",
SuspectService.getById(Long.valueOf(id)));
return "info";
}
#RequestMapping("/suspects")
public String objectsList(#RequestParam(value = "page", required = false) Integer page,
#RequestParam(value = "sortBy", required = false) String sortBy,
#RequestParam(value = "sortType", required = false) String sortType, Model model) {
int size = 5;
if (page == null)
page = 1;
if(sortBy==null)
sortBy ="name";
if(sortType==null)
sortType ="asc";
Page<Suspect> pageSuspect = SuspectService.findAll(page, size, sortBy, sortType);
List<Suspect> suspects = pageSuspect.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", pageSuspect.getTotalPages());
model.addAttribute("totalItems", pageSuspect.getTotalElements());
model.addAttribute("sortBy", sortBy);
model.addAttribute("sortType", sortType);
model.addAttribute("reverseSortDir", sortType.equals("asc") ? "desc" : "asc");
model.addAttribute("suspects", suspects);
var suspect = new Suspect();
model.addAttribute("suspect", suspect);
return "suspects";
}
}

Jackson XML - Can't deserialize Boolean

I'm trying to parse XML content with Jackson but I have some difficulties with Boolean value.
This is a part of my XML content :
<?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body xmlns:ns1="http://somecontent">
<wsResponse xmlns="http://somecontent">
<responseType>SUCCESS</responseType>
<response>
<successfullResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterchangeSearchResponse">
<totalResult>1</totalResult>
<returnedResults>1</returnedResults>
<pageIndex>1</pageIndex>
<interchanges>
<interchange>
<depositId>somecontent</depositId>
<interchangeId>somecontent</interchangeId>
<depositDate>2021-03-26T11:45:05.000+01:00</depositDate>
<depositSubject>dépôt WS</depositSubject>
<numADS>number</numADS>
<adsDate>2021-03-26T11:45:05.000+01:00</adsDate>
<alias>contentAsString</alias>
<version xsi:nil="true"/>
<isTest>false</isTest>
<deposantAccount>
<name>someString</name>
</deposantAccount>
<teleProcedure>someString</teleProcedure>
<statesHistory>
This is my XML structure as class :
#JacksonXmlProperty(localName = "Body")
private Body body;
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
}
#JacksonXmlRootElement
public class Body {
#JacksonXmlProperty(localName = "Fault")
private Fault fault;
private WsResponse wsResponse;
public Fault getFault() {
return fault;
}
public void setFault(Fault fault) {
this.fault = fault;
}
public WsResponse getWsResponse() {
return wsResponse;
}
public void setWsResponse(WsResponse wsResponse) {
this.wsResponse = wsResponse;
}
}
#JacksonXmlRootElement(localName = "wsResponse")
public class WsResponse {
private String responseType;
private Response response;
public String getResponseType() {
return responseType;
}
public void setResponseType(String responseType) {
this.responseType = responseType;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
}
#JacksonXmlRootElement
public class Response {
private SuccessfulResponse successfullResponse;
private ErrorResponse errorResponse;
public SuccessfulResponse getSuccessfullResponse() {
return successfullResponse;
}
public void setSuccessfullResponse(SuccessfulResponse successfullResponse) {
this.successfullResponse = successfullResponse;
}
public ErrorResponse getErrorResponse() {
return errorResponse;
}
public void setErrorResponse(ErrorResponse errorResponse) {
this.errorResponse = errorResponse;
}
}
#JacksonXmlRootElement
public class SuccessfulResponse {
/**
* Response when add document success
*/
private String depositId;
/**
* Response when get interchanges by deposit id success
*/
private String type;
private Integer totalResult;
private Integer returnedResults;
private Integer pageIndex;
private Interchanges interchanges;
/**
* Response when get declaration details success
*/
private DeclarationTdfc declarationTdfc;
public SuccessfulResponse() {}
public SuccessfulResponse(String depositId) {
this.depositId = depositId;
}
public SuccessfulResponse(String type, Integer totalResult, Integer returnedResults, Integer pageIndex, Interchanges interchanges) {
this.type = type;
this.totalResult = totalResult;
this.returnedResults = returnedResults;
this.pageIndex = pageIndex;
this.interchanges = interchanges;
}
public SuccessfulResponse(DeclarationTdfc declarationTdfc) {
this.declarationTdfc = declarationTdfc;
}
public SuccessfulResponse(String depositId, String type, Integer totalResult, Integer returnedResults,
Integer pageIndex, Interchanges interchanges, DeclarationTdfc declarationTdfc) {
super();
this.depositId = depositId;
this.type = type;
this.totalResult = totalResult;
this.returnedResults = returnedResults;
this.pageIndex = pageIndex;
this.interchanges = interchanges;
this.declarationTdfc = declarationTdfc;
}
public String getDepositId() {
return depositId;
}
public void setDepositId(String depositId) {
this.depositId = depositId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getTotalResult() {
return totalResult;
}
public void setTotalResult(Integer totalResult) {
this.totalResult = totalResult;
}
public Integer getReturnedResults() {
return returnedResults;
}
public void setReturnedResults(Integer returnedResults) {
this.returnedResults = returnedResults;
}
public Integer getPageIndex() {
return pageIndex;
}
public void setPageIndex(Integer pageIndex) {
this.pageIndex = pageIndex;
}
public Interchanges getInterchanges() {
return interchanges;
}
public void setInterchanges(Interchanges interchanges) {
this.interchanges = interchanges;
}
public DeclarationTdfc getDeclarationTdfc() {
return declarationTdfc;
}
public void setDeclarationTdfc(DeclarationTdfc declarationTdfc) {
this.declarationTdfc = declarationTdfc;
}
}
public class Interchanges {
#JacksonXmlProperty(localName = "interchange")
#JacksonXmlElementWrapper(useWrapping = false)
private List<Interchange> interchange;
public Interchanges() { super(); }
public Interchanges(List<Interchange> interchange) {
super();
this.interchange = interchange;
}
public List<Interchange> getInterchange() {
return interchange;
}
public void setInterchange(List<Interchange> interchange) {
this.interchange = interchange;
}
}
public class Interchange {
private String depositId;
private Integer interchangeId;
//#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = SelfmedConstants.Dates.ENGLISH_DATETIME_PATTERN)
private String depositDate;
private String depositSubject;
private String numADS;
//#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = SelfmedConstants.Dates.ENGLISH_DATETIME_PATTERN)
private String adsDate;
private String alias;
//#JacksonXmlProperty(isAttribute = true)
private String version;
#JsonSerialize(using = BooleanSerializer.class)
#JsonDeserialize(using = BooleanDeserializer.class)
#JacksonXmlProperty(localName = "isTest")
private Boolean isTest;
#JacksonXmlProperty(localName = "name")
#JacksonXmlElementWrapper(localName = "deposantAccount")
private List<String> name;
private String teleProcedure;
private StatesHistory statesHistory;
#JacksonXmlProperty(localName = "declarationId")
#JacksonXmlElementWrapper(localName = "declarationIds")
private List<String> declarationId;
public Interchange() {
}
public Interchange(String depositId, Integer interchangeId, String depositDate, String depositSubject, String numADS,
String adsDate, String alias, String version, Boolean isTest, List<String> name, String teleProcedure,
StatesHistory statesHistory, List<String> declarationId) {
this();
this.depositId = depositId;
this.interchangeId = interchangeId;
this.depositDate = depositDate;
this.depositSubject = depositSubject;
this.numADS = numADS;
this.adsDate = adsDate;
this.alias = alias;
this.version = version;
this.isTest = isTest;
this.name = name;
this.teleProcedure = teleProcedure;
this.statesHistory = statesHistory;
this.declarationId = declarationId;
}
public String getDepositId() {
return depositId;
}
public void setDepositId(String depositId) {
this.depositId = depositId;
}
public Integer getInterchangeId() {
return interchangeId;
}
public void setInterchangeId(Integer interchangeId) {
this.interchangeId = interchangeId;
}
public String getDepositDate() {
return depositDate;
}
public void setDepositDate(String depositDate) {
this.depositDate = depositDate;
}
public String getDepositSubject() {
return depositSubject;
}
public void setDepositSubject(String depositSubject) {
this.depositSubject = depositSubject;
}
public String getNumADS() {
return numADS;
}
public void setNumADS(String numADS) {
this.numADS = numADS;
}
public String getAdsDate() {
return adsDate;
}
public void setAdsDate(String adsDate) {
this.adsDate = adsDate;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Boolean getIsTest() {
return isTest;
}
public void setIsTest(Boolean isTest) {
this.isTest = isTest;
}
public void setIsTest(String isTest) {
this.isTest = Boolean.valueOf(isTest);
}
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
public String getTeleProcedure() {
return teleProcedure;
}
public void setTeleProcedure(String teleProcedure) {
this.teleProcedure = teleProcedure;
}
public StatesHistory getStatesHistory() {
return statesHistory;
}
public void setStatesHistory(StatesHistory statesHistory) {
this.statesHistory = statesHistory;
}
public List<String> getDeclarationId() {
return declarationId;
}
public void setDeclarationId(List<String> declarationId) {
this.declarationId = declarationId;
}
}
As you can see in the Interchange class, I try some stuff but nothing work.
I generate my class like that :
JacksonXmlModule xmlModule = new JacksonXmlModule();
xmlModule.setDefaultUseWrapper(false);
XmlMapper xmlMapper = new XmlMapper(xmlModule);
xmlMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
xmlMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES);
System.out.println(responseAsString);
Envelope envelope = xmlMapper.readValue(responseAsString, new TypeReference<>() {
But when I try to parse my content, I got this exception :
Cannot construct instance of payload.response.Interchange (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('false')
at [Source: (StringReader); line: 20, column: 28] (through reference chain: payload.response.Envelope["Body"]->payload.response.Body["wsResponse"]->payload.response.WsResponse["response"]->payload.response.Response["successfullResponse"]->payload.response.SuccessfulResponse["interchanges"]->payload.response.Interchanges["interchange"]->java.util.ArrayList[1])
I try a lot of things but nothing work so I'm wondering if the problem may be not here...
If you have any solution or leads to explore, please let me know !
Thank.
According to JavaBean spec you have to name getter isTest().

Android NetworkBoundResource using repository pattern API error

Hi I am just started with mvvm with repository pattern I am stuck with the API error
I am using the generic API Response class as per google official repo in java
/**
* Generic class for handling responses from Retrofit
*
* #param <T>
*/
public class ApiResponse<T> {
public ApiResponse<T> create(Throwable error) {
return new ApiErrorResponse<>(error.getMessage().equals("") ? error.getMessage() : "Unknown error\nCheck network connection");
}
public ApiResponse<T> create(Response<T> response) {
if (response.isSuccessful()) {
T body = response.body();
if (body instanceof GithubApiResponse) {
if (AppUtils.isValid((GithubApiResponse) body)) {
String errorMsg = "Empty Response.";
return new ApiErrorResponse<>(errorMsg);
}
}
if (body == null || response.code() == 204) { // 204 is empty response
return new ApiEmptyResponse<>();
} else {
return new ApiSuccessResponse<>(body);
}
} else {
String errorMsg = "";
try {
errorMsg = response.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
errorMsg = response.message();
}
return new ApiErrorResponse<>(errorMsg);
}
}
/**
* Generic success response from api
*
* #param <T>
*/
public class ApiSuccessResponse<T> extends ApiResponse<T> {
private T body;
ApiSuccessResponse(T body) {
this.body = body;
}
public T getBody() {
return body;
}
}
/**
* Generic Error response from API
*
* #param <T>
*/
public class ApiErrorResponse<T> extends ApiResponse<T> {
private String errorMessage;
ApiErrorResponse(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
}
/**
* separate class for HTTP 204 resposes so that we can make ApiSuccessResponse's body non-null.
*/
public class ApiEmptyResponse<T> extends ApiResponse<T> {
}
}
and My networkBoundResource as like this
public abstract class NetworkBoundResource<ResultType, RequestType> {
private AppExecutors appExecutors;
private MediatorLiveData<Resource<ResultType>> results = new MediatorLiveData<>();
public NetworkBoundResource(AppExecutors appExecutors) {
this.appExecutors = appExecutors;
init();
}
private void init() {
// update LiveData for loading status
results.setValue((Resource<ResultType>) Resource.loading(null));
// observe LiveData source from local db
final LiveData<ResultType> dbSource = loadFromDb();
results.addSource(dbSource, new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
results.removeSource(dbSource);
if (shouldFetch(ResultType)) {
// get data from the network
fetchFromNetwork(dbSource);
} else {
results.addSource(dbSource, new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
setValue(Resource.success(ResultType));
}
});
}
}
});
}
/**
* 1) observe local db
* 2) if <condition/> query the network
* 3) stop observing the local db
* 4) insert new data into local db
* 5) begin observing local db again to see the refreshed data from network
*
* #param dbSource
*/
private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
Timber.d("fetchFromNetwork: called.");
// update LiveData for loading status
results.addSource(dbSource, new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
setValue(Resource.loading(ResultType));
}
});
final LiveData<ApiResponse<RequestType>> apiResponse = createCall();
results.addSource(apiResponse, new Observer<ApiResponse<RequestType>>() {
#Override
public void onChanged(#Nullable final ApiResponse<RequestType> requestObjectApiResponse) {
results.removeSource(dbSource);
results.removeSource(apiResponse);
/*
3 cases:
1) ApiSuccessResponse
2) ApiErrorResponse
3) ApiEmptyResponse
*/
if (requestObjectApiResponse instanceof ApiResponse.ApiSuccessResponse) {
Timber.d("onChanged: ApiSuccessResponse.");
appExecutors.diskIO().execute(new Runnable() {
#Override
public void run() {
// save the response to the local db
saveCallResult((RequestType) processResponse((ApiResponse.ApiSuccessResponse) requestObjectApiResponse));
appExecutors.mainThread().execute(new Runnable() {
#Override
public void run() {
results.addSource(loadFromDb(), new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
setValue(Resource.success(ResultType));
}
});
}
});
}
});
} else if (requestObjectApiResponse instanceof ApiResponse.ApiEmptyResponse) {
Timber.d("onChanged: ApiEmptyResponse");
appExecutors.mainThread().execute(new Runnable() {
#Override
public void run() {
results.addSource(loadFromDb(), new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
setValue(Resource.success(ResultType));
}
});
}
});
} else if (requestObjectApiResponse instanceof ApiResponse.ApiErrorResponse) {
Timber.d("onChanged: ApiErrorResponse.");
results.addSource(dbSource, new Observer<ResultType>() {
#Override
public void onChanged(#Nullable ResultType ResultType) {
setValue(
Resource.error(
((ApiResponse.ApiErrorResponse) requestObjectApiResponse).getErrorMessage(),
ResultType
)
);
}
});
}
}
});
}
private ResultType processResponse(ApiResponse.ApiSuccessResponse response) {
return (ResultType) response.getBody();
}
private void setValue(Resource<ResultType> newValue) {
if (results.getValue() != newValue) {
results.setValue(newValue);
}
}
// Called to save the result of the API response into the database.
#WorkerThread
protected abstract void saveCallResult(#NonNull RequestType item);
// Called with the data in the database to decide whether to fetch
// potentially updated data from the network.
#MainThread
protected abstract boolean shouldFetch(#Nullable ResultType data);
// Called to get the cached data from the database.
#NonNull
#MainThread
protected abstract LiveData<ResultType> loadFromDb();
// Called to create the API call.
#NonNull
#MainThread
protected abstract LiveData<ApiResponse<RequestType>> createCall();
// Returns a LiveData object that represents the resource that's implemented
// in the base class.
public final LiveData<Resource<ResultType>> getAsLiveData() {
return results;
}
}
my Repository looks like this
#Singleton
public class GithubRepository {
private GithubDao githubDao;
private GithubTrendingApiService githubApiService;
public GithubRepository(GithubDao githubDao, GithubTrendingApiService githubApiService) {
this.githubDao = githubDao;
this.githubApiService = githubApiService;
}
public LiveData<Resource<List<GithubEntity>>> getRepositories() {
return new NetworkBoundResource<List<GithubEntity>, GithubApiResponse>(AppExecutors.getInstance()) {
#Override
protected void saveCallResult(#NonNull GithubApiResponse item) {
List<GithubEntity> repositories = item.getItems();
githubDao.insertRepositories(repositories);
}
#Override
protected boolean shouldFetch(#Nullable List<GithubEntity> data) {
// Timber.d("shouldFetch: repo: " + data.toString());
// int currentTime = (int) (System.currentTimeMillis() / 1000);
// Timber.d("shouldFetch: current time: " + currentTime);
// int lastRefresh = data.getTimestamp();
// Timber.d("shouldFetch: last refresh: " + lastRefresh);
// Timber.d("shouldFetch: it's been " + ((currentTime - lastRefresh) / 60 / 60 / 24) +
// " days since this recipe was refreshed. 30 days must elapse before refreshing. ");
// if ((currentTime - data.getTimestamp()) >= Constants.RECIPE_REFRESH_TIME) {
// Timber.d("shouldFetch: SHOULD REFRESH RECIPE?! " + true);
// return true;
// }
// Timber.d("shouldFetch: SHOULD REFRESH RECIPE?! " + false);
return true;
}
#NonNull
#Override
protected LiveData<List<GithubEntity>> loadFromDb() {
return githubDao.getTrendingRepository();
}
#NonNull
#Override
protected LiveData<ApiResponse<GithubApiResponse>> createCall() {
return githubApiService.fetchTrendingRepositories();
}
}.getAsLiveData();
}
}
Can anyone tell me whats I am doing wrong because I am getting an API error? somehow I am not able to get the correct data that's why the database is also not filling up
my GithubAPi response class is this
public class GithubApiResponse {
public GithubApiResponse() {
this.items = new ArrayList<>();
}
public GithubApiResponse(List<GithubEntity> items) {
this.items = items;
}
private List<GithubEntity> items;
public List<GithubEntity> getItems() {
return items;
}
public void setItems(List<GithubEntity> items) {
this.items = items;
}
}
and the Entity class is this
#Entity
public class GithubEntity implements Parcelable {
public GithubEntity(#NonNull Long id, String author, String name, String avatar,
String url, String description, Integer stars, Integer forks, Integer currentPeriodStars, String language, String languageColor) {
this.id = id;
this.author = author;
this.name = name;
this.avatar = avatar;
this.url = url;
this.description = description;
this.stars = stars;
this.forks = forks;
this.currentPeriodStars = currentPeriodStars;
this.language = language;
this.languageColor = languageColor;
}
#NonNull
#PrimaryKey
private Long id;
#SerializedName("author")
#Expose
private String author;
#SerializedName("name")
#Expose
private String name;
#SerializedName("avatar")
#Expose
private String avatar;
#SerializedName("url")
#Expose
private String url;
#SerializedName("description")
#Expose
private String description;
#SerializedName("stars")
#Expose
private Integer stars;
#SerializedName("forks")
#Expose
private Integer forks;
#SerializedName("currentPeriodStars")
#Expose
private Integer currentPeriodStars;
#SerializedName("language")
#Expose
private String language;
#SerializedName("languageColor")
#Expose
private String languageColor;
#NonNull
public Long getId() {
return id;
}
public void setId(#NonNull Long id) {
this.id = id;
}
protected GithubEntity(Parcel in) {
author = in.readString();
name = in.readString();
avatar = in.readString();
url = in.readString();
description = in.readString();
if (in.readByte() == 0) {
stars = null;
} else {
stars = in.readInt();
}
if (in.readByte() == 0) {
forks = null;
} else {
forks = in.readInt();
}
if (in.readByte() == 0) {
currentPeriodStars = null;
} else {
currentPeriodStars = in.readInt();
}
language = in.readString();
languageColor = in.readString();
}
public static final Creator<GithubEntity> CREATOR = new Creator<GithubEntity>() {
#Override
public GithubEntity createFromParcel(Parcel in) {
return new GithubEntity(in);
}
#Override
public GithubEntity[] newArray(int size) {
return new GithubEntity[size];
}
};
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStars() {
return stars;
}
public void setStars(Integer stars) {
this.stars = stars;
}
public Integer getForks() {
return forks;
}
public void setForks(Integer forks) {
this.forks = forks;
}
public Integer getCurrentPeriodStars() {
return currentPeriodStars;
}
public void setCurrentPeriodStars(Integer currentPeriodStars) {
this.currentPeriodStars = currentPeriodStars;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getLanguageColor() {
return languageColor;
}
public void setLanguageColor(String languageColor) {
this.languageColor = languageColor;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(author);
dest.writeString(name);
dest.writeString(avatar);
dest.writeString(url);
dest.writeString(description);
if (stars == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(stars);
}
if (forks == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(forks);
}
if (currentPeriodStars == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(currentPeriodStars);
}
dest.writeString(language);
dest.writeString(languageColor);
}
}
can anyone guide me so that I can fix this issue? stuck here for 2 days

How to load the Client Details by ClientId AND ClientSecret - OAuth 2.0

I am using my own implementation of ClientDetailsServiceConfigurer so I am doing like this:
OAuthConfig.java
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(this.customClientDetailsManager);
}
CustomClientDetailsManager.java
#Service
public class CustomClientDetailsManager implements ClientDetailsService {
final static Logger log = LoggerFactory.getLogger(CustomClientDetailsManager.class);
private final CustomerService customerService;
#Inject
public CustomClientDetailsManager(final CustomerService customerService) {
this.customerService = customerService;
}
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
final Customer customer = customerService.getCustomerByClientId(clientId);
log.debug("****** Customer is: " + customer.getClientId());
log.debug("****** Customer Secret is: " + customer.getClientSecret());
log.debug("****** Client ID Coming from Request is: " + clientId);
final BaseClientDetails details = new BaseClientDetails();
details.setClientId(clientId);
log.debug("*** Client id: " + clientId );
details.setAuthorizedGrantTypes(Arrays.asList(customer.getAuthorizedGrantTypes()));
log.debug("*** AuthorizedGrantTypes: " + Arrays.asList(customer.getAuthorizedGrantTypes()));
details.setScope(Arrays.asList(customer.getScope()));
log.debug("*** Scope: " + Arrays.asList(customer.getScope()));
details.setResourceIds(Arrays.asList(customer.getResourceIds()));
log.debug("*** ResourceIds: " + Arrays.asList(customer.getResourceIds()));
final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(customer.getAuthorities()));
details.setAuthorities(authorities);
authorities.forEach(authority -> {
log.debug("*** Authority: " + authority);
});
log.debug("Returning details...");
return details;
}
So basically I am grabing my ClientId through loadClientByClientId(String clientId) but I want a method that allows me to grab my Client ID and Client Secret.
Any clues? Thanks
Finally I got a solution.
You need to create a 'CustomClientDetails' that implements 'ClientDetails' and return it.
For example:
public class CustomClientDetails implements ClientDetails {
final static Logger log = LoggerFactory.getLogger(CustomClientDetailsManager.class);
private static final long serialVersionUID = 6725149038554040628L;
private Customer customer;
public CustomClientDetails(final Customer customer) {
this.customer = customer;
}
#Override
public Integer getAccessTokenValiditySeconds() {
return customer.getAccessTokenValidity();
}
#Override
public Map<String, Object> getAdditionalInformation() {
final Set<String> additionalInformation = new HashSet<String>();
additionalInformation.add(customer.getAdditionalInformation());
return null;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(customer.getAuthorities()));
return authorities;
}
#Override
public Set<String> getAuthorizedGrantTypes() {
final Set<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add(customer.getAuthorizedGrantTypes());
return authorizedGrantTypes;
}
#Override
public String getClientId() {
return customer.getClientId();
}
#Override
public String getClientSecret() {
return customer.getClientSecret();
}
#Override
public Integer getRefreshTokenValiditySeconds() {
return customer.getRefreshTokenValidity();
}
#Override
public Set<String> getRegisteredRedirectUri() {
final Set<String> registeredRedirectUris = new HashSet<String>();
registeredRedirectUris.add(customer.getWebServerRedirectUri());
return registeredRedirectUris;
}
#Override
public Set<String> getResourceIds() {
final Set<String> resourcesIds = new HashSet<String>();
resourcesIds.add(customer.getResourceIds());
return resourcesIds;
}
#Override
public Set<String> getScope() {
final Set<String> scopes = new HashSet<String>();
scopes.add(customer.getScope());
return scopes;
}
#Override
public boolean isAutoApprove(String scope) {
return false; //TODO: for some reason this is always false
}
#Override
public boolean isScoped() {
return true; //TODO: for some reason this is always true
}
#Override
public boolean isSecretRequired() {
return true; //TODO: for some reason this is always true
}
}
public class CustomClientDetailsManager implements ClientDetailsService {
final static Logger log = LoggerFactory.getLogger(CustomClientDetailsManager.class);
private final CustomerService customerService;
#Inject
public CustomClientDetailsManager(final CustomerService customerService) {
this.customerService = customerService;
}
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
final Customer customer = customerService.getCustomerByClientId(clientId);
final CustomClientDetails customClientDetails = new CustomClientDetails(customer);
return customClientDetails;
}

Select JsonView in the Spring MVC Controller

I'm currently writing a REST api using Jackson (2.4.0-rc3) and spring mvc (4.0.3), and I'm trying to make it secure.
In this way, I try to use JsonView to select the parts of the objects that can be serialized.
I've found the solution (which is not for me) to annotate my Controller method with the view I want. But I'd like to select on the fly the view inside the controller.
Is it possible to extend the ResponseEntity class in order to specify which JsonView I want ?
A little piece of code :
Here is the account class
public class Account {
#JsonProperty(value = "account_id")
private Long accountId;
#JsonProperty(value = "mail_address")
private String mailAddress;
#JsonProperty(value = "password")
private String password;
#JsonProperty(value = "insert_event")
private Date insertEvent;
#JsonProperty(value = "update_event")
private Date updateEvent;
#JsonProperty(value = "delete_event")
private Date deleteEvent;
#JsonView(value = PublicView.class)
public Long getAccountId() {
return accountId;
}
#JsonView(value = PublicView.class)
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
#JsonView(value = OwnerView.class)
public String getMailAddress() {
return mailAddress;
}
#JsonView(value = OwnerView.class)
public void setMailAddress(String mailAddress) {
this.mailAddress = mailAddress;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonView(value = OwnerView.class)
public void setPassword(String password) {
this.password = password;
}
#JsonView(value = AdminView.class)
public Date getInsertEvent() {
return insertEvent;
}
#JsonView(value = AdminView.class)
public void setInsertEvent(Date insertEvent) {
this.insertEvent = insertEvent;
}
#JsonView(value = AdminView.class)
public Date getUpdateEvent() {
return updateEvent;
}
#JsonView(value = AdminView.class)
public void setUpdateEvent(Date updateEvent) {
this.updateEvent = updateEvent;
}
#JsonView(value = AdminView.class)
public Date getDeleteEvent() {
return deleteEvent;
}
#JsonView(value = OwnerView.class)
public void setDeleteEvent(Date deleteEvent) {
this.deleteEvent = deleteEvent;
}
#JsonProperty(value = "name")
public abstract String getName();
}
Here is the account controller
#RestController
#RequestMapping("/account")
public class AccountCtrlImpl implements AccountCtrl {
#Autowired
private AccountSrv accountSrv;
public AccountSrv getAccountSrv() {
return accountSrv;
}
public void setAccountSrv(AccountSrv accountSrv) {
this.accountSrv = accountSrv;
}
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getById(accountId), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/get_by_mail_address/{mail_address}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getByMailAddress(#PathVariable(value = "mail_address") String mailAddress) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getByMailAddress(mailAddress), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/authenticate/{mail_address}/{password}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> authenticate(#PathVariable(value = "mail_address") String mailAddress, #PathVariable(value = "password") String password) {
return new ResponseEntity<Account>(HttpStatus.NOT_IMPLEMENTED);
}
}
I really like the solution presented here to dynamically select a json view inside your controller method.
Basically, you return a MappingJacksonValue which you construct with the value you want to return. After that you call setSerializationView(viewClass) with the proper view class. In my use case, I returned a different view depending on the current user, something like this:
#RequestMapping("/foos")
public MappingJacksonValue getFoo(#AuthenticationPrincipal UserDetails userDetails ) {
MappingJacksonValue value = new MappingJacksonValue( fooService.getAll() );
if( userDetails.isAdminUser() ) {
value.setSerializationView( Views.AdminView.class );
} else {
value.setSerializationView( Views.UserView.class );
}
return value;
}
BTW: If you are using Spring Boot, you can control if properties that have no view associated are serialized or not by setting this in your application.properties:
spring.jackson.mapper.default_view_inclusion=true
I've solved my problem extending ResponseEntity like this :
public class ResponseViewEntity<T> extends ResponseEntity<ContainerViewEntity<T>> {
private Class<? extends BaseView> view;
public ResponseViewEntity(HttpStatus statusCode) {
super(statusCode);
}
public ResponseViewEntity(T body, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, BaseView.class), statusCode);
}
public ResponseViewEntity(T body, Class<? extends BaseView> view, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, view), statusCode);
}
}
and ContainerViewEntity encapsulate the object and the selected view
public class ContainerViewEntity<T> {
private final T object;
private final Class<? extends BaseView> view;
public ContainerViewEntity(T object, Class<? extends BaseView> view) {
this.object = object;
this.view = view;
}
public T getObject() {
return object;
}
public Class<? extends BaseView> getView() {
return view;
}
public boolean hasView() {
return this.getView() != null;
}
}
After that, we have convert only the object with the good view.
public class JsonViewMessageConverter extends MappingJackson2HttpMessageConverter {
#Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (object instanceof ContainerViewEntity && ((ContainerViewEntity) object).hasView()) {
writeView((ContainerViewEntity) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}
protected void writeView(ContainerViewEntity view, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType());
ObjectWriter writer = this.getWriterForView(view.getView());
JsonGenerator jsonGenerator = writer.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writer.writeValue(jsonGenerator, view.getObject());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private ObjectWriter getWriterForView(Class<?> view) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
return mapper.writer().withView(view);
}
}
And to finish, I enable the converter
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="wc.handler.view.JsonViewMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
And that's it, I can select the View in the controller
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseViewEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) throws ServiceException {
return new ResponseViewEntity<Account>(this.getAccountSrv().getById(accountId), PublicView.class, HttpStatus.OK);
}
FYI, Spring 4.1 already supported using #JsonView directly on #ResponseBody and ResponseEntity:
Jackson’s #JsonView is supported directly on #ResponseBody and ResponseEntity controller methods for serializing different amounts of detail for the same POJO (e.g. summary vs. detail page). This is also supported with View-based rendering by adding the serialization view type as a model attribute under a special key.
And in http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-jsonview you can find the much simpler solution:
#RestController
public class UserController {
#RequestMapping(value = "/user", method = RequestMethod.GET)
#JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
#JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
This works great :
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public void getZone(#PathVariable long id, #RequestParam(name = "tree", required = false) boolean withChildren, HttpServletResponse response) throws IOException {
LOGGER.debug("Get a specific zone with id {}", id);
Zone zone = zoneService.findById(id);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (withChildren) {
response.getWriter().append(mapper.writeValueAsString(zone));
} else {
response.getWriter().append(mapper.writerWithView(View.ZoneWithoutChildren.class).writeValueAsString(zone));
}
}

Categories