Angular 5 + Spring boot image upload - java

I am trying to upload an image from angular to spring boot rest. but it throws
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:190) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
I am new to both the technologies. Help me upload the file.
Here is the code.
RestController
public ResponseMessage addUser(#RequestParam("profileImage") MultipartFile profileImage,
#RequestParam("user") User user) {
try {
if (userService.doesUserExist(user.getUsername())) {
return new ResponseMessage("The username is not available; please choose a different username", 200);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
String extension = FilenameUtils.getExtension(profileImage.getOriginalFilename());
storageService.storeFile(profileImage, user.getUsername() + "." + extension, profilePicturePath);
user.setProfilePicturePath(profilePicturePath + "/" + user.getUsername() + "." + extension);
userService.addUser(user);
return new ResponseMessage(user.getUsername() + " was added successfully!", 200);
}
Angular Service
addUser(user:User,profileImage:File){
const formData:FormData = new FormData();
formData.append('profileImage', profileImage);
formData.append('user',JSON.parse(JSON.stringify(user)));
return this.http.post<ResponseMessage>("/api/admin/user/new_user",formData,this.postHttpOptions);
}
Angular Component
setImage(files:FileList){
this.newProfileImage = files.item(0);
}
upload(){
if (this.newUser.password == this.newUser.confirmPassword) {
this.userService.addUser(this.newUser,this.newProfileImage).subscribe(
res => {
this.responseMessage = res;
alert(this.responseMessage.message);
},
err => {
alert("Error Adding the user: " + err.message);
}
);
}
}
Angular Template
<input type='file' id="imgInp" (change)="setImage($event.target.files)" />

I got it.
The changes I made..
Instead of #RequestParam I added #Requestpart
Instead of #RequestParam User user I added #RequestPart String user; then converted String to user using ObjectMapper.
On the client side, I removed'ContentType = application/Json' and mentioned no content type
Here is the code
public String addUser(#RequestPart("profileImage") MultipartFile profileImage,
#RequestPart("user") String userString) throws JsonParseException, JsonMappingException, IOException {
User user = new ObjectMapper().readValue(userString, User.class);
}

Related

How to specify filename when using MailGun API

I am currently in the process of integrating MailGun into one of my applications. For my use cases I need to be able to send out attachments. So far, I have been able to send out attachments just fine but my problem is that I am unable to specify the attachment's name. Their documentation found here specifies that the attachment part should be added when including attachment, but does not state how to specify the file's name.
For reference I am using Spring's RestTemplate as my client and I am reading the file as a base64 encoded string which is then trasnformed into a ByteArrayResource. For reference my code is this:
#Override
public EmailDocument sendEmail(EmailDocument email) {
var properties = propProvider.findFor(email.getCompany());
var parts = new LinkedMultiValueMap<String, Object>();
parts.add("from", email.getFrom());
parts.add("to", toCommaString(email.getTo()));
if (!email.getCc().isEmpty()) {
parts.add("cc", toCommaString(email.getCc()));
}
if (!email.getBcc().isEmpty()) {
parts.add("bcc", toCommaString(email.getBcc()));
}
parts.add("subject", email.getSubject());
if (email.getIsHtml()) {
parts.add("html", email.getBody());
} else {
parts.add("text", email.getBody());
}
email.getAttachments().forEach(attachment -> {
var decoded = Base64.getDecoder().decode(attachment.getBytes(StandardCharsets.UTF_8));
parts.add("attachment", new ByteArrayResource(decoded));
});
var header = headerProvider.createHeader("api", properties.getApiKey(), inferMediaType(email));
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parts, header);
try {
var response = restTemplate.exchange(createDomain(properties.getDomain()), HttpMethod.POST, request, MailGunApiResponse.class);
log.info("Got the following MailGun response {}", response);
if (!response.getStatusCode().is2xxSuccessful()) {
email.setFailureReason(Optional.ofNullable(response.getBody()).map(MailGunApiResponse::getMessage).orElse(null));
email.setRetries(email.getRetries() + 1);
email.setFailed(isFailed(email));
} else {
email.setSent(true);
}
} catch (Exception e) {
log.error("An error has occurred while attempting to send out email {}", email, e);
email.setFailureReason(e.getMessage());
email.setRetries(email.getRetries() + 1);
email.setFailed(isFailed(email));
}
return email;
}
Does anyone know how to specify a filename for the attachment?

Cannot redirect after facebook login using response entity in spring boot

Ok, so I am trying to achieve facebook authentication in my application but after success user is not redirected to homepage even though location header is set.
This is my current scenario.
Front-End call:
#GetMapping(value = "/login/facebook")
public String loginFacebook() {
String facebookLoginUrl = String.valueOf(this.restTemplate.getForEntity(this.serverApi + "/login/facebook",String.class).getHeaders().get("Location"));
facebookLoginUrl = facebookLoginUrl.substring(1,facebookLoginUrl.length()-1);
System.out.println(facebookLoginUrl);
return "redirect:" + facebookLoginUrl;
}
Back-End:
#RequestMapping(value = "/login/facebook", method = RequestMethod.GET)
public String startFacebookProcess() {
this.facebookConnectionFactory = new FacebookConnectionFactory(appId,appSecret);
OAuth2Operations operations = this.facebookConnectionFactory.getOAuthOperations();
OAuth2Parameters parameters = new OAuth2Parameters();
// promeni url-a za front-enda
parameters.setRedirectUri("http://localhost:8080/login/returnFromFacebook");
parameters.setScope(this.scope);
System.out.println("In startFacebookProcess");
String url = operations.buildAuthorizeUrl(parameters);
System.out.println(url);
return "redirect:" + url;
}
After that, the user is being redirected to the official facebook login page where he allows his details to be used.
Then he performs a GET request to my Front-End with his authorization code:
#GetMapping(value = "/login/returnFromFacebook")
public ResponseEntity getFacebookData(#RequestParam("code") String authorizationCode){
System.out.println(authorizationCode);
ResponseEntity response = this.restTemplate.getForEntity(this.serverApi +
"/login/returnFromFacebook?code=" + authorizationCode, ResponseEntity.class);
if(response.getStatusCode().is2xxSuccessful()){
return response;
}
return new ResponseEntity(HttpStatus.CONFLICT);
}
My Back-End is being called and I contact facebook to fetch the user data using his authorization code. Then I return a ResponseEntity which inherited all the HttpServletResponse headers and cookies.
#RequestMapping(value = "/login/returnFromFacebook", method = RequestMethod.GET)
public ResponseEntity getDataFromFacebook(#RequestParam("code") String authorizationCode,
HttpServletResponse response) throws IOException {
// leg 2
OAuth2Operations operations = this.facebookConnectionFactory.getOAuthOperations();
AccessGrant accessGrant = operations.exchangeForAccess(authorizationCode,
"http://localhost:8080/login/returnFromFacebook",null);
// leg 3
System.out.println("code: " + authorizationCode);
Connection<Facebook> connection = this.facebookConnectionFactory.createConnection(accessGrant);
User currentUser = connection.getApi().fetchObject("me", User.class,"email,first_name,last_name");
System.out.println("Email: " + currentUser.getEmail());
if(this.appUserDAO.findUserAccountByEmail(currentUser.getEmail()) == null){
Map<String, String> facebookDetailsMap = new LinkedHashMap<>();
facebookDetailsMap.put("email",currentUser.getEmail());
facebookDetailsMap.put("name",currentUser.getFirstName() + " " + currentUser.getLastName());
this.appUserDAO.saveFacebookAccount(facebookDetailsMap);
}
// Create JWT Token
String token = JWT.create()
.withSubject(currentUser.getEmail())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.sign(HMAC512(JwtProperties.SECRET.getBytes()));
JwtAuthenticationFilter.setJwtCookie(response,token);
System.out.println(response.getStatus() + " " + response.getHeader("set-cookie"));
response.setHeader("Location", "http://localhost:8080/");
// HttpHeaders headers = new HttpHeaders();
// headers.add(index");
return new ResponseEntity<>(HttpStatus.CREATED);
}
This is the received response header in the browser. The cookies are set and everything but user is not being redirected to Location header. :
I tried to return a String "redirect:/" from GetFacebookData(Front-End) but the response cookies and headers are not being applied. I don't know why the Location header is not working.
I fixed it by returning the 'index' template if the back-end call was sucessful but also apply the cookie header into the original response (which was to the front-end) using HttpServletResponse (this is not possible with ResponseEntity btw). With HttpServletResponse you dont have to return it to the browser, it applies everything by default. With ResponseEntity you have to return ResponseEntity Object in order to apply headers. This is how the function looks like now:
#GetMapping(value = "/login/returnFromFacebook")
public String getFacebookData(#RequestParam("code") String authorizationCode, HttpServletResponse httpServletResponse) throws IOException {
System.out.println(authorizationCode);
ResponseEntity response = this.restTemplate.getForEntity(this.serverApi +
"/login/returnFromFacebook?code=" + authorizationCode, ResponseEntity.class);
System.out.println(response.getStatusCodeValue() + " " + response.getHeaders().get("Location"));
//set cookie for original request
String jwtCookieHeader = String.valueOf(response.getHeaders().get("set-cookie"));
jwtCookieHeader = jwtCookieHeader.substring(1,jwtCookieHeader.length()-1);
httpServletResponse.setHeader("set-cookie", jwtCookieHeader);
if(response.getStatusCode().is2xxSuccessful()){
return "redirect:/";
}
return String.valueOf(new ResponseEntity(HttpStatus.CONFLICT));
}

Spring Boot #RestController no respond

I'm starting with Spring and REST application. Currently, I'm developing one application on my own and I stuck.
The app is divided just like standard Spring Boot project. All of the controllers are contained in web package.
One of "standard" controller is responsible for handling HTTP request and returning an HTML website. I have added a REST controller which should respond to POST request from the first controller, but I receive a 404 error.
How it looks like in code?
#RestController
#RequestMapping("/users")
public class UserRestController {
#Autowired
private UserService userService;
#RequestMapping(value = "/user", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public ResponseEntity<?> getUser(#RequestParam("userId") String userId, Errors errors) {
AjaxUser response = new AjaxUser();
if (errors.hasErrors()) {
response.message = errors.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.joining(","));
return ResponseEntity.badRequest().body(response);
}
response.setUser(userService.getUserById(Integer.getInteger(userId).intValue()));
return ResponseEntity.ok(response);
}
private class AjaxUser {
private User user;
private String message;
public void setUser(User user) {
this.user = user;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
#Override
public String toString() {
return "User { id:" + user.getId() + ", Name: " + user.getName() + ", surname: " + user.getSurname() + "}";
}
}
}
From .js file I send a ajax query which should trigger a rest controller, here is the code:
function sendUserId(id) {
var user = {};
user["userId"] = id;
console.log("USER: ", user);
$.ajax({
type: "POST",
contentType: "application/json",
url: "/users/user",
data: JSON.stringify(user),
dataType: 'json',
cache: false,
timeout: 100000,
success: function (user) {
var json = "<h4>Ajax Response</h4><pre>"
+ JSON.stringify(user, null, 4) + "</pre>";
console.log("SUCCESS : ", user);
},
error: function (e) {
var json = "<h4>Ajax Response</h4><pre>"
+ e.responseText + "</pre>";
console.log("ERROR : ", e);
}
});
}
userId is taken from a html by jQuery, console.log show existing and right value.
Note: There exist a standard user #Controller which is responsible for displaying a user list, it works, problem appear during sending a user id to REST controller. It behaves just like the REST controller doesn't exist and browser return 404 status response. Btw, page use a Spring Secure to login and so on.
Could someone help?
BR Konrad
The controller is looking to have a request parameter that you are missing in the js requesting url
/users/user?userId=1
You can get a user by id like below:
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public ResponseEntity<User> get(#PathVariable("id") int id) {
User user = userService.findById(id);
if (user == null) {
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
So your rest entry point is /users/userid, eg: /users/1
Found this from the post Spring MVC RESTFul Web Service CRUD Example
the problem based on function arguments, REST controller should take String argument and next parse it to JSON object, the response should be String too. Topic can be closed, thanks all to be involved.

Delete File from Disk using AJAX and Spring Boot

I am trying to delete a file from the disk ( Local or Server ) using Ajax and Spring boot technologies.
So far i have tried this:
Ajax/jquery:
$(".ct-color-specs").on("click",".color-spec-file-delete",function() {
var deletedFileName = $(this).parents(".ct-attached-color-spec-files").find("a").text();
$.ajax({
url : "/Application/removeFile/"+deletedFileName",
type: 'DELETE',
success: function (res) {
console.log(data);
}
});
});
Controller:
#RequestMapping(value = "/removeFile",produces="text/html", method = RequestMethod.DELETE)
public String removeFileHandler(#PathVariable("deletedFileName") String filepath, Model model) {
String removeFileCheck = "false";
try{
System.out.println("Delete filepath from AJX");
File file = new File(filepath);
if(file.delete()){
System.out.println(file.getName() + " is deleted!");
removeFileCheck="true";
}else{
System.out.println("Delete operation is failed.");
}
}catch(Exception e){
e.printStackTrace();
}
model.addAttribute("checkList", removeFileCheck);
return "p/view";
}
Error:
"Not Found" message : "No message available" path :
"/Application/removeFile/File.pdf" status : 404
You have written the #RequestMapping(value = "/removeFile" ...) in wrong format
Path variable in spring is to be used like below
#RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)
public String getLogin(#PathVariable("userId") String userId,
#PathVariable("roleId") String roleId){
System.out.println("User Id : " + userId);
System.out.println("Role Id : " + roleId);
return "hello";
}
I will be writing answer here , as i have solved with below code.
Controller:
#RequestMapping(value = "/removeFile/{deletedFileName}", method = RequestMethod.GET)
public String removeFileHandler(#PathVariable("deletedFileName") String filepath, Model model) {
.....
}
AJAX/jquery:
$(".ct-color-specs").on("click",".color-spec-file-delete",function() {
var deletedFileName = $(this).parents(".ct-attached-color-spec-files").find("a").text().split('/').pop().split('\\').pop();;
alert("deletedFileName--->" + deletedFileName);
$.ajax({
url : "/Application/removeFile/"+deletedFileName,
type: 'GET',
success: function (res) {
console.log(data);
}
});
});

415 (Unsupported Media Type) with jquery

I am new to using a RESTful API and I don't know why it is showing this error.
I am posting the values through jQuery. Do I need to do something else? This is my jQuery code:
Updated : Now it is showing 405 (Method Not Allowed)
$(document).ready(function(){
$("#patsubmit").click(function() {
var firstName = $("#firstName").val();
var lastName = $("#lastName").val();
var mobileNumber = $("#mobileNumber").val();
var emailId = $("#emailId").val();
var dataString = '{"firstName":"'+ firstName + '","lastName":"' + lastName + '","mobileNumber":"' + mobileNumber + '", "emailId":"' + emailId+'"}';
console.log(dataString);
if(firstName=='' )
{
alert("nothing in it");
}
else
{
$.ajax({
type: 'POST',
url : '/geniedoc/api/patient/register',
data: dataString,
contentType: 'application/json',
dataType: 'json',
headers: {'Content-Type':'application/json'}
success: function(){ // Uncaught SyntaxError: Unexpected identifier
console.log();
}
});}
return false;
});
});
This is my Java API. MAIN_PATIENT = api/patient and RestURIConstants.REGISTER = register
#RestController
#RequestMapping(value = RestURIConstants.MAIN_PATIENT)
public class PatientRestController extends AbstractController implements RestURIConstants, GenieDocConstants{
private static final Logger logger = Logger.getLogger(UserRestController.class);
#RequestMapping(value = RestURIConstants.REGISTER, method = RequestMethod.POST, consumes ="application/json")
public #ResponseBody ModelMap registerPatient(HttpServletRequest request, #RequestBody PatientVo patientVo){
logger.info("registerPatient : Start");
long startTime = System.currentTimeMillis();
ModelMap map = new ModelMap();
PatientVo patVo;
try {
if(patientVo.getFirstName() == null) {
map.addAttribute(STATUS_CODE, FAILURE);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(MESSAGE_FIRST_NOT_EMPTY));
} else if(patientVo.getEmailId() == null) {
map.addAttribute(STATUS_CODE, FAILURE);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(MESSAGE_EMAIL_NOT_EMPTY));
} else if(patientVo.getEmailId() == "") {
map.addAttribute(STATUS_CODE, FAILURE);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(MESSAGE_EMAIL_NOT_EMPTY));
} else if (patientVo.getMobileNumber() == null) {
map.addAttribute(STATUS_CODE, FAILURE);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(MESSAGE_MOBILE_NOT_EMPTY));
} else {
patVo = this.patientManagementService.provisionPatient(patientVo);
if (patVo != null) {
map.addAttribute("patientId", patVo.getEmailId());
map.addAttribute(STATUS_CODE, SUCCESS_STATUS_CODE_REGPATIENT);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(SUCCESS_STATUS_CODE_REGPATIENT));
} else {
map.addAttribute(STATUS_CODE, ERROR_STATUS_CODE_REG);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(ERROR_STATUS_CODE_REG));
}
}
} catch (MongoDBDocumentNotFoundException e) {
map.addAttribute(STATUS_CODE, ERROR_STATUS_CODE_REGPATIENT);
map.addAttribute(STATUS_MESSAGE,this.env.getProperty(ERROR_STATUS_CODE_REGPATIENT));
logger.error("Error : " + e.getMessage());
//e.printStackTrace();
} catch (UserAreadyExsistException e) {
map.addAttribute(STATUS_CODE, ERROR_STATUS_CODE_REGPATIENT);
map.addAttribute(STATUS_MESSAGE, this.env.getProperty(ERROR_STATUS_CODE_REGPATIENT));
logger.error("Error : " + e.getMessage());
//e.printStackTrace();
}
logger.debug("Exit: Total Time Taken: "+ (System.currentTimeMillis() - startTime));
return map;
}
You need to set the Content-Type Header to application/json
$.ajax({
type: 'POST',
url: '/geniedoc/api/patient/register',
data: dataString,
headers: {
'Content-Type':'application/json'
}
.....
}
In your spring controller you are defining, that only content of MIME Type application/json is accepted. Because standard content type text/plain the Spring controller does not accept your request and send back a status code 415 (Media type not supported)
Edit: As user6409738 mentioned, you need to send your data in json format. Otherwise the Spring Controller will cause an exception parsing the body.
For example the solution posted by Yagnesh Agola
var dataString = '{"firstName":"'+ firstName + '","lastName":"' + lastName + '","mobileNumber":"' + mobileNumber + '","emailId":' + emailId+'"}';
It depends what your PatientVo Class is looking like
Data you have send to server from client is not in JSON format.
var dataString = 'firstName='+ firstName + '&lastName=' + lastName + '&mobileNumber=' + mobileNumber + '&emailId=' + emailId;
Above line is used to send data string to server which is not in JSON format it is simple query string.
Either you can convert above string in JSON format
var dataString = '{"firstName":"'+ firstName + '","lastName":"' + lastName + '","mobileNumber":"' + mobileNumber + '","emailId":"' + emailId+'"}';
OR
You can directly submit form data in JSON using below code.
var formData = JSON.stringify($("#myForm").serializeArray());
add contentType parameter when use jQuery Ajax
$.ajax({
type : "POST",
contentType : 'application/json',
url : "/geniedoc/api/patient/register",
data : JSON.stringify({"param1" : "param1", "param2":2})
})
remove consumer = "application/json" in your request-mapping definition, (it's not necessary, SpringMVC will auto detect right converter)
because U use #RequestBody in springMVC controller, then SpringMVC will convert the argument with RequestResponseBodyMethodProcessor which use default converters to resolve the argument. default converters list is:
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
For your situation, MappingJackson2HttpMessageConverter is expect to be used to resolve the argument. And here is the definition of MappingJackson2HttpMessageConverter, it need MediaType of application/json
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_JSON_UTF8,
new MediaType("application", "*+json", DEFAULT_CHARSET));
}

Categories