'Spring Boot + Angular - MultipartException: Current request is not a multipart request
I'm having an issue using Angular 9 with Spring Boot for a simple application that uploads files along with data from the UI, in the same request. Until I've implemented security with basic authentication, everything worked just fine. Now, after I'm logged in and want to upload data, I get the following error:
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
with the headers set up to Content-Type: 'multipart/form-data'
and the Spring Controller using MultipartFile. The strange thing is that the GET request works well, with the exception it's content type is application/json
. If I'm disabling the http-interceptor, the error becomes Access to XMLHttpRequest at 'http://localhost:8080/pacients' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
. I've also tried every workaround for handling CORS, both Angular and Spring related, with no success.
Angular component for uploading the file:
pacient: Pacient;
pacientForm: FormGroup = new PacientCreateFormBuilder().build();
submitPromise: Promise<Pacient>;
onSubmit() {
if(this.pacientForm.valid) {
const formData: FormData = new FormData();
formData.append('pacientFile', <File>this.pacientForm.value.pacientFile);
formData.append('newPacient', new Blob([JSON.stringify(this.pacientForm.value)], {type: "application/json"}));
this.submitPromise = this.pacientCreateService.save(formData);
} else {
ValidationUtils.markFormAsDirty(this.pacientForm);
}
}
Angular service for upload:
public save(formData: FormData) {
var headers = new HttpHeaders(
{
'Content-Type': 'multipart/form-data',
'Authorization': `Basic ${window.btoa(this.authService.username + ":" + this.authService.password)}`
}
);
return this.httpClient.post<Pacient>("http://localhost:8080/pacient", formData, {headers: headers})
.toPromise();
}
Angular authentication service:
authenticate(username: String, password: String) {
return this.http.get(`http://localhost:8080/auth`, {
headers: { authorization: this.createBasicAuthToken(username, password) }}).pipe(map((res) => {
this.username = username;
this.password = password;
this.registerInSession(username, password);
}));
}
createBasicAuthToken(username: String, password: String) {
return 'Basic ' + window.btoa(username + ":" + password);
}
registerInSession(username, password) {
sessionStorage.setItem(this.SESSION_KEY, username);
}
Angular http-interceptor:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.isUserLoggedin() && req.url.indexOf('basicauth') === -1) {
const request = req.clone({
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Basic ${window.btoa(this.authService.username + ":" + this.authService.password)}`
})
});
return next.handle(request);
}
return next.handle(req);
}
Spring Security config:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = new StandardPasswordEncoder();
auth
.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER")
.and()
.withUser("admin")
.password("admin")
.roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().
disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
Spring Controller:
@PostMapping("/pacient")
public Pacient create(@RequestPart("pacientFile") MultipartFile pacientFile, @RequestPart("newPacient") PacientDTO pacientDTO)
EDIT: If I'm using @PostMapping(value = "/pacient", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
in the Controller, the error is changing and appears only on browser's console and sais
Access to XMLHttpRequest at 'http://localhost:8080/pacient' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In order to get past it, I updated the Controller with @CrossOrigin(origins = {"http://localhost:4200"})
, added the following fields to the headers from service
'Access-Control-Allow-Headers': `Content-Type`,
'Access-Control-Allow-Methods': `POST`,
'Access-Control-Allow-Origin': `*`
and also created a proxy.conf.json file with
{
"/": {
"target": "http://localhost:8080",
"secure": false
}
}
and added it to package.json, to start with "start": "ng serve --proxy-config proxy.conf.json"
and added CORS configuration in my Spring Security config class
@Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
but still no luck...
Solution 1:[1]
I just came across this issue myself and it was a really dumb error on my end but I thought I should share in case someone made the same mistake. I had just moved the file over to my VM and my user account did not have access to the file... Once I changed the permissions to the file, it worked as expected.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Josh D |