I have already created an e mail confirmation in spring boot it is working nicely, and also I created a link when a user click it should say "confirmed"but I did not figure it out how to do that?
E mail sender java class:
#Component
public class EmailSender {
#Autowired
JavaMailSender javaEmailSender;
public void sendEmail(String to, String subject, String text) throws MessagingException {
MimeMessage message = javaEmailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text, true);
helper.addInline("logo.jpg", new ClassPathResource("./images/logo.jpg"));
javaEmailSender.send(message);
}
}
E mail template Loader:
#Component
public class EmailTemplateLoader {
#Autowired
private Configuration freemakerConfiguration;
public String getEmailBody(String name, String confirmationLink) throws TemplateException {
try {
Template template = freemakerConfiguration.getTemplate("EmailConfirmation.ftl");
Map<String, Object> data = new HashMap<String, Object>();
data.put("name", name);
data.put("confirmationLink", confirmationLink);
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return "";
}
}
My signup Resource :
#Autowired
private SignupService signupService;
#Autowired
private HttpServletRequest httpServletRequest;
#RequestMapping(value = "user/signup", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> signup(#RequestBody UserType user) throws SignUpException {
URL requestUrl = null;
try {
requestUrl = new URL(httpServletRequest.getRequestURL().toString());
} catch (MalformedURLException e) {
logger.debug("Malformed Request Url");
}
logger.debug(requestUrl.toString());
signupService.signUp(user, requestUrl.getHost());
return new ResponseEntity<>(HttpStatus.OK);
}
#RequestMapping(value = "user/confirmation", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> confirmSignUp(#RequestParam("u") String loginName, #RequestParam("p") String token) {
try {
signupService.emailConfirmation(loginName, token);
return new ResponseEntity<>(HttpStatus.OK);
} catch (SignUpException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
So, As I told I can send an email successfully, but I could not sort how I can write confirmation successfully
do you want to send confirmation mail or just want to know confirmation status that mail has been sent? Please specify it.
You Can add some code in your SignUpService where you have implemented
emailConfiguration() method
#Autowired
private Configuration freemarkerConfiguration;
and add like this in method to get verification
Template t = freemarkerConfiguration.getTemplate("verification.ftl");
String text = FreeMarkerTemplateUtils.processTemplateIntoString(t,object);
or you can do something with ResponseEntity while returning confirmSignup() method You can add some DTO also to get confirmation status.
Related
The content from MockMvc managed to have status code but missing the data.
Test class:
#Test
public void shouldReturnAll() throws Exception {
when(userService.getAll()).thenReturn(users); // note that 'users' is not empty, I already checked.
MvcResult response = this.mvc.perform(get("/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(2)))
.andReturn();
}
Reponse:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"status":"success"}
Forwarded URL = null
Redirected URL = null
Cookies = []
I think it has something to do with Response object in my Controller class.
Controller:
#GetMapping
public ResponseEntity<Response> getAll() {
List<User> users = userService.getAll();
Response resp = new Response(StatusMessage.SUCCESS, users);
return new ResponseEntity<Response>(resp, HttpStatus.OK);
}
Edit: Another test which works (getting a single User):
#Test
public void getUserByIdTest() throws Exception {
when(this.userService.getUserById(any(Long.class))).thenReturn(user);
MvcResult response = this.mvc.perform(get("/users/{id}", user.getId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id", is(user.getId().intValue())))
.andExpect(jsonPath("$.data.name", is(user.getName())))
.andReturn();
}
Controller:
#GetMapping(value = "/{id}")
public ResponseEntity<Response> getUserById(#PathVariable Long id) throws Exception {
try {
User user = userService.getUserById(id);
Response resp = new Response(StatusMessage.SUCCESS, user);
return new ResponseEntity<Response>(resp, HttpStatus.OK);
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
Response object class:
#Data
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Response<T> {
private String status;
private T data;
public Response(StatusMessage status, T data) {
this.status = status.getStatusMessage();
this.data = data;
}
This is my Controller class and I wrote exceptions to check pin and username(because I want to make them unique) But When I enter data in Postman It gives only one of them( I want to create scenario that both pin and username are not unique and used for once) How can I write Both of them same time. I actually want to add some elements additionally like that so I'm stuck there)
I want output be like:
Ex: This pin Already in use!
This username Already in use!
#RestController
#RequestMapping("/api")
public class EmployeeController {
//private EmployeeService employeeService;
private final EmployeeRepository employeeRepository;
// ControllerException controllerException;
#Autowired
public EmployeeController(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
#PostMapping("/employee")
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee, BindingResult bindingResult) throws EmployeeAlreadyExistsException {
if (employeeRepository.existsById(employee.getId())) {
throw new EmployeeAlreadyExistsException();
}
String temUsername = employee.getUsername();
if(temUsername !=null && !"".equals(temUsername)) {
Employee userObject = employeeRepository.findByUsername(temUsername);
if(userObject!=null) {
throw new EmployeeUsernameAlreadyExistsException();
}
}
String pin = employee.getPin();
if(pin !=null && !"".equals(pin)) {
Employee userObject = employeeRepository.findByPin(pin);
if(userObject!=null) {
throw new EmployeePinAlreadyExistsException();
}
}
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
#GetMapping("/employees")
public ResponseEntity<List<Employee>> getAllEmployee() throws EmployeeNotFoundException {
return new ResponseEntity<>((List<Employee>) employeeRepository.findAll(), HttpStatus.OK);
}
#PostMapping("/update")
public ResponseEntity<?> updateEmployee(#RequestBody Employee employee) throws EmployeeNotFoundException, EmployeePinAlreadyExistsException {
if (!employeeRepository.existsById(employee.getId())) {
throw new EmployeeNotFoundException();
} else {
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
}
#GetMapping("employee/{id}")
public ResponseEntity<?> getEmployeeById(#PathVariable("id") int id, Employee employee) throws EmployeeNotFoundException {
if (!employeeRepository.existsById(employee.getId())) {
throw new EmployeeNotFoundException();
}
return new ResponseEntity<>(employeeRepository.findById(id), HttpStatus.OK);
}
#ExceptionHandler(value = EmployeeAlreadyExistsException.class)
public ResponseEntity<?> EmployeeAlreadyExistsException(EmployeeAlreadyExistsException employeeAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This Employee already exist!")
.code("101")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeeNotFoundException.class)
public ResponseEntity<?> EmployeeNotFoundException(EmployeeNotFoundException employeeNotFoundException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This id is not valid!")
.code("404")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeePinAlreadyExistsException.class)
public ResponseEntity<?> EmployeePinAlreadyExistsException(EmployeePinAlreadyExistsException employeePinAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This pin Already in use!")
.code("101")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeeUsernameAlreadyExistsException.class)
public ResponseEntity<?> EmployeeUsernameAlreadyExistsException(EmployeeUsernameAlreadyExistsException employeeUsernameAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This Username Already in use!")
.code("109")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
}
You should define a class with attribute like private List<String> errMsg to handle response
Then, check pin and username and save check result to boolean variable. If result are false, add an element to errMsg like "pin already in use", "username already in use".
Finally, dependent to check results, if all of them are false, you can throw PinAndUsernameAlreadyExistsException with errMsg have 2 messages were added
If you need all the fields in the exception message don't throw the exception until all validations have been made, however for a public accessed application is not a good idea from a security perspective, It's better a generic error message like: "Some of your employee data is already used", anyway, if you need it for some kind of customer requirement or simple formation exercise, this code should do what you ask for:
private void assertEmployeeDataisUnique(Employee employee) throws Exception {
List<String> existingFields = new ArrayList<>();
if (employeeRepository.existsById(employee.getId())) {
existingFields.add("id");
}
String temUsername = employee.getUsername();
if(temUsername !=null && !"".equals(temUsername)) {
Employee userObject = employeeRepository.findByUsername(temUsername);
if(userObject!=null) {
existingFields.add("username");
}
}
String pin = employee.getPin();
if(pin !=null && !"".equals(pin)) {
Employee userObject = employeeRepository.findByPin(pin);
if(userObject!=null) {
existingFields.add("pin");
}
}
if(!existingFields.isEmpty()) {
throw new Exception("Employee data is not unique. Fields: " + existingFields);
}
}
#PostMapping("/employee")
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee, BindingResult bindingResult) throws EmployeeAlreadyExistsException {
this.assertEmployeeDataisUnique(employee);
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
i want to test my register controller and i'm getting and error bcz posted data doesn,t tranform to my model and test fall on validation.
#PostMapping(value = "/register", consumes = "application/json")
#ResponseStatus(HttpStatus.CREATED)
private String postRegistration( #ModelAttribute #Valid final UserCreateFormDto user, final BindingResult result,
final RedirectAttributes redirectAttributes, final WebRequest webRequest) {
if (result.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
redirectAttributes.addFlashAttribute("user", user);
return "redirect:/register";
}
if (userService.checkEmailExist(user.getEmail())) {
return "redirect:/register?exist";
}
final User registered = userService.createNewUserAccount(user);
try {
final String appUrl = webRequest.getContextPath();
eventPublisher.publishEvent(new RegistrationCompleteEvent(registered, webRequest.getLocale(), appUrl));
return "redirect:/login?success";
} catch (UserNotExistsException e) {
return "redirect:/register";
}
}
and test
#Test
public void shouldReturnHttp201WhenUserIsCreated() throws Exception {
//given
final UserCreateFormDto userCreateFormDto = createUserCreateForm();
final User user = createUser();
given(userService.checkEmailExist(userCreateFormDto.getEmail())).willReturn(false);
given(userService.createNewUserAccount(any(UserCreateFormDto.class))).willReturn(user);
//when
final MockHttpServletResponse response = mockMvc
.perform(post("/register").with(csrf())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(userCreateFormDto)))
.andReturn()
.getResponse();
//then
assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());
assertThat(response.getContentAsString()).isEqualTo(userJacksonTester.write(user).getJson());
verify(userService).createNewUserAccount(refEq(userCreateFormDto));
}
I think this photo should explain as easy as it can
Edit
I tried to mock BindingResult but after reading more it wasn't a good idea.
I have following controller:
#Controller
#RequestMapping(path = "/foo/bar")
public classMyController
#PostMapping(path = "/acction")
#ResponseStatus(value = HttpStatus.OK)
public String doAction(#RequestBody final MyDto request) {
try {
someService.resetPassword(request.getSomeData());
return "Success";
} catch (MyException e) {
return String.format("redirect:google.com");
}
}
The line return "Success"; Looks redundant. Is there way to avoid it?
Change your return type to ResponseEntity class and set response status code inplace of String "success".
#GetMapping("/hello")
ResponseEntity<String> hello() {
return new ResponseEntity<>("Hello World!", HttpStatus.OK);
}
I have created a Spring Restful Service and Spring MVC application.
Restful Service ::
Restful service returns an entity if its existing in DB. If it doesn't exist It returns a custom Exception information in ResponseEntity object.
It is working as expected tested using Postman.
#GetMapping(value = "/validate/{itemId}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public ResponseEntity<MyItem> validateItem(#PathVariable Long itemId, #RequestHeader HttpHeaders httpHeaders) {
MyItem myItem = myitemService.validateMyItem(itemId);
ResponseEntity<MyItem> responseEntity = null;
if (myItem == null) {
throw new ItemNotFoundException("Item Not Found!!!!");
}
responseEntity = new ResponseEntity<MyItem>(myItem, headers, HttpStatus.OK);
return responseEntity;
}
If the requested Entity does not exist Restful Service returns below.
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<ExceptionResponse>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
But when I am calling the above service from a spring MVC application using RestTemplate, It is returning a valid object if it exists.
If the requested object does not exist Restful service is returning the exception information but its not reaching the calling(spring MVC) application.
Spring MVC application calls Restful Web Service using Rest template
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
This is expected behavior. Rest template throws exception when the http status is client error or server error and returns the response when http status is not error status.
You have to provide implementation to use your error handler, map the response to response entity and throw the exception.
Create new error exception class with ResponseEntity field.
public class ResponseEntityErrorException extends RuntimeException {
private ResponseEntity<ErrorResponse> errorResponse;
public ResponseEntityErrorException(ResponseEntity<ErrorResponse> errorResponse) {
this.errorResponse = errorResponse;
}
public ResponseEntity<ErrorResponse> getErrorResponse() {
return errorResponse;
}
}
Custom error handler which maps the error response back to ResponseEntity.
public class ResponseEntityErrorHandler implements ResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters;
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return hasError(response.getStatusCode());
}
protected boolean hasError(HttpStatus statusCode) {
return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpMessageConverterExtractor<ExceptionResponse> errorMessageExtractor =
new HttpMessageConverterExtractor(ExceptionResponse.class, messageConverters);
ExceptionResponse errorObject = errorMessageExtractor.extractData(response);
throw new ResponseEntityErrorException(ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(errorObject));
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
}
RestTemplate Configuration - You have to set RestTemplate's errorHandler to ResponseEntityErrorHandler.
#Configuration
public class RestTemplateConfiguration {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntityErrorHandler errorHandler = new ResponseEntityErrorHandler();
errorHandler.setMessageConverters(restTemplate.getMessageConverters());
restTemplate.setErrorHandler(errorHandler);
return restTemplate;
}
}
Calling Method
#Autowired restTemplate
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
try {
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
} catch (ResponseEntityErrorException re) {
ResponseEntity<ErrorResponse> errorResponse = re.getErrorResponse();
}
Try using the #ResponseBody annotation on your Exceptionhandler. e.g:
public #ResponseBody ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {... }
You should use Custom Exception Handler to fix your case. It looks like this
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public CustomResponseEntityExceptionHandler() {
super();
}
// 404
#ExceptionHandler(value = { EntityNotFoundException.class, ResourceNotFoundException.class })
protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {
BaseResponse responseError = new BaseResponse(HttpStatus.NOT_FOUND.value(),HttpStatus.NOT_FOUND.name(),
Constants.HttpStatusMsg.ERROR_NOT_FOUND);
logger.error(ex.getMessage());
return handleExceptionInternal(ex, responseError, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}
And your code should throw some exception, eg:
if (your_entity == null) {
throw new EntityNotFoundException("said something");
}
If you get this case in somewhere else again, you just throw exception like above. Your handler will take care the rest stuffs.
Hope this help.
I've started your application and works just fine.
Maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
The controller class is :
#Controller
public class ValidationController {
#GetMapping(value = "/validate/{itemId}")
public #ResponseBody ResponseEntity<MyItem> validateItem(#PathVariable Long itemId) {
if (itemId.equals(Long.valueOf(1))) {
throw new ItemNotFoundException();
}
return new ResponseEntity<>(new MyItem(), HttpStatus.OK);
}
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
}
and the test:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ValidationController.class, secure = false)
public class TestValidationController {
#Autowired
private MockMvc mockMvc;
#Test
public void testExpectNotFound() throws Exception {
mockMvc.perform(get("/validate/1"))
.andExpect(status().isNotFound());
}
#Test
public void testExpectFound() throws Exception {
mockMvc.perform(get("/validate/2"))
.andExpect(status().isOk());
}
}
Are you sure the url you are trying to use with RestTemplate is correct?
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
Your get method is #GetMapping(value = "/validate/{itemId}"
If you don't have request mapping at the level of the controller the url should be:
http://localhost:8080/validate/1
Another difference is the missing #ResponseBody on your controller method.