Spring Boot + Angular - MultipartException: текущий запрос не является составным запросом - PullRequest
0 голосов
/ 28 марта 2020

У меня проблема с использованием Angular 9 с Spring Boot для простого приложения, которое загружает файлы вместе с данными из пользовательского интерфейса в том же запросе. Пока я не реализовал безопасность с аутентификацией basi c, все работало просто отлично. Теперь, после того, как я вошел в систему и хочу загрузить данные, я получаю следующую ошибку:

org.springframework.web.multipart.MultipartException: Current request is not a multipart request

с заголовками, установленными на Content-Type: 'multipart/form-data' и Spring Controller, использующий MultipartFile. Странно то, что запрос GET работает хорошо, за исключением того, что его тип контента application/json. Если я отключаю http-перехватчик, ошибка становится 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. Я также пробовал каждый обходной путь для обработки CORS, как Angular, так и Spring, безуспешно.

Angular компонент для загрузки файла:

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 сервис для загрузки:

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 сервис аутентификации:

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-перехватчик:

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();
    }

Пружинный контроллер:

@PostMapping("/pacient")
    public Pacient create(@RequestPart("pacientFile") MultipartFile pacientFile, @RequestPart("newPacient") PacientDTO pacientDTO)

РЕДАКТИРОВАТЬ: Если я использую @PostMapping(value = "/pacient", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) в контроллере, ошибка меняется и появляется только на консоли браузера и sais

Доступ к XMLHttpRequest в 'http://localhost: 8080 / pacient ' from origin 'http://localhost: 4200 ' заблокирован политикой CORS : На запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin».

Чтобы обойти его, я обновил контроллер с помощью @CrossOrigin(origins = {"http://localhost:4200"}), добавил следующие поля в заголовки из сервиса

'Access-Control-Allow-Headers': `Content-Type`,
'Access-Control-Allow-Methods': `POST`,
'Access-Control-Allow-Origin': `*`

, а также создали файл proxy.conf. json с

{
  "/": {
    "target": "http://localhost:8080",
    "secure": false
  }
}

и добавили его в пакет. * 10 66 *, для начала "start": "ng serve --proxy-config proxy.conf.json" и добавили конфигурацию CORS в мой класс конфигурации Spring Security

@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;
    }

, но все равно не повезло ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...