Error on upload: response to preflight request doesn't pass access control check #493

Open
opened 2016-11-16 07:33:56 +00:00 by Myrmex · 1 comment
Myrmex commented 2016-11-16 07:33:56 +00:00 (Migrated from github.com)

I'm trying to use this control in an Angular2 SPA with an ASP.NET core server-side web API.

On the server side, I'm following the example at https://www.janaks.com.np/file-upload-asp-net-core-web-api/. Essentially, my controller's code is like this:

[Route("api/assets")]
public sealed class AssetController : Controller
{
	// ...
	[HttpPost]
	[Route("{id}/content")]
	public async Task<IActionResult> Upload(string id, IFormFile file)
	{
	    Stream stream = file.OpenReadStream();
	    // TODO do something with the stream...
	    return Ok();
	}
}

If I test this with Postman it works as expected. Then, on the client side I have an Angular2 cli template where I did the following to install:

1.npm install ng2-file-upload --save.
2.in app.module.ts add import { FileUploadModule } from 'ng2-file-upload'; and add FileUploadModule to declarations.

I then used the demo's code (https://github.com/valor-software/ng2-file-upload/tree/development/demo) as the model for my component, like this (it does not seem that FileItem is included in FileUploadModule, so I'm using an any type):

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FileUploader, ParsedResponseHeaders } from 'ng2-file-upload';
import { SettingsService } from '../../services/settings.service';
@Component({
  selector: 'app-asset-uploader',
  templateUrl: './asset-uploader.component.html',
  styleUrls: ['./asset-uploader.component.css']
})
export class AssetUploaderComponent {
  @Input() public set assetId(value: string) {
    if (this._assetId === value) {
      return;
    }
    this._assetId = value;
    this.uploader = new FileUploader({
      url: `${this._settings.apiBaseUrl}assets/${this.assetId}/content`
    });
    // TODO: find out how to include FileItem type from file-item.class in ng2-file-upload
    this.uploader._onCompleteItem = (item: any, response: string, status: number,
      headers: ParsedResponseHeaders) => {
      this.uploaded.emit(item.file.size);
    };
  }
  public get assetId(): string {
    return this._assetId;
  }
  @Output() uploaded: EventEmitter<number> = new EventEmitter<number>();
  public uploader: FileUploader;
  private _assetId: string;
  constructor(private _settings: SettingsService) {
    this.uploaded = new EventEmitter<number>();
  }
}

The corresponding view template is equal to that of your demo, except for the fact that I'm allowing only a single file upload:

<div class="row">
  <div class="col-md-3">
    <h3>Select file</h3>
    <div ng2FileDrop [uploader]="uploader" class="well">
      drop here
    </div>
    <input type="file" ng2FileSelect [uploader]="uploader" />
  </div>
  <div class="col-md-9" style="margin-bottom: 40px">
    <h3>Queue</h3>
    <p>Length: {{ uploader?.queue?.length }}</p>
    <table class="table">
      <thead>
        <tr>
          <th width="50%">name</th>
          <th>size</th>
          <th>progress</th>
          <th>status</th>
          <th>actions</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let item of uploader.queue">
          <td><strong>{{ item?.file?.name }}</strong></td>
          <td *ngIf="uploader.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
          <td *ngIf="uploader.isHTML5">
            <div class="progress" style="margin-bottom: 0;">
              <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div>
            </div>
          </td>
          <td class="text-center">
            <span *ngIf="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
            <span *ngIf="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
            <span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
          </td>
          <td nowrap>
            <button type="button" class="btn btn-success btn-xs" (click)="item.upload()"
                    [disabled]="item.isReady || item.isUploading || item.isSuccess">
              <span class="glyphicon glyphicon-upload"></span> upload
            </button>
            <button type="button" class="btn btn-warning btn-xs" (click)="item.cancel()"
                    [disabled]="!item.isUploading">
              <span class="glyphicon glyphicon-ban-circle"></span> cancel
            </button>
            <button type="button" class="btn btn-danger btn-xs" (click)="item.remove()">
              <span class="glyphicon glyphicon-trash"></span> remove
            </button>
          </td>
        </tr>
      </tbody>
    </table>
    <div>
      <div>
        Queue progress:
        <div class="progress">
          <div class="progress-bar"
               role="progressbar"
               [ngStyle]="{ 'width': uploader.progress + '%' }"></div>
        </div>
      </div>
      <button type="button" class="btn btn-success btn-s"
              (click)="uploader.uploadAll()"
              [disabled]="!uploader.getNotUploadedItems().length">
        <span class="glyphicon glyphicon-upload"></span> upload all
      </button>
      <button type="button" class="btn btn-warning btn-s"
              (click)="uploader.cancelAll()" [disabled]="!uploader.isUploading">
        <span class="glyphicon glyphicon-ban-circle"></span> cancel all
      </button>
      <button type="button" class="btn btn-danger btn-s"
              (click)="uploader.clearQueue()" [disabled]="!uploader.queue.length">
        <span class="glyphicon glyphicon-trash"></span> remove all
      </button>
    </div>
  </div>
</div>

Now, when I pick a file to upload and launch the upload, I immediately get this error from the server:

Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute

If I place a breakpoint in my upload method on server side, it never gets hit. Further, the error message looks deceiving, as I'm using CORS but I'm not allowing any wildcard. Here is my startup code for it (Startup.cs, Configure method):

app.UseCors(builder =>
        builder.WithOrigins("http://localhost:4200")
            .AllowAnyHeader()
            .AllowAnyMethod());

The only relevant reference I found googling for this error message is this: https://github.com/filepicker/filepicker-js/issues/42, which was related to a bug. Could you help?

Thanks!

I'm trying to use this control in an Angular2 SPA with an ASP.NET core server-side web API. On the server side, I'm following the example at https://www.janaks.com.np/file-upload-asp-net-core-web-api/. Essentially, my controller's code is like this: [Route("api/assets")] public sealed class AssetController : Controller { // ... [HttpPost] [Route("{id}/content")] public async Task<IActionResult> Upload(string id, IFormFile file) { Stream stream = file.OpenReadStream(); // TODO do something with the stream... return Ok(); } } If I test this with Postman it works as expected. Then, on the client side I have an Angular2 cli template where I did the following to install: 1.`npm install ng2-file-upload --save`. 2.in `app.module.ts` add `import { FileUploadModule } from 'ng2-file-upload';` and add `FileUploadModule` to declarations. I then used the demo's code (https://github.com/valor-software/ng2-file-upload/tree/development/demo) as the model for my component, like this (it does not seem that `FileItem` is included in `FileUploadModule`, so I'm using an `any` type): import { Component, Input, Output, EventEmitter } from '@angular/core'; import { FileUploader, ParsedResponseHeaders } from 'ng2-file-upload'; import { SettingsService } from '../../services/settings.service'; @Component({ selector: 'app-asset-uploader', templateUrl: './asset-uploader.component.html', styleUrls: ['./asset-uploader.component.css'] }) export class AssetUploaderComponent { @Input() public set assetId(value: string) { if (this._assetId === value) { return; } this._assetId = value; this.uploader = new FileUploader({ url: `${this._settings.apiBaseUrl}assets/${this.assetId}/content` }); // TODO: find out how to include FileItem type from file-item.class in ng2-file-upload this.uploader._onCompleteItem = (item: any, response: string, status: number, headers: ParsedResponseHeaders) => { this.uploaded.emit(item.file.size); }; } public get assetId(): string { return this._assetId; } @Output() uploaded: EventEmitter<number> = new EventEmitter<number>(); public uploader: FileUploader; private _assetId: string; constructor(private _settings: SettingsService) { this.uploaded = new EventEmitter<number>(); } } The corresponding view template is equal to that of your demo, except for the fact that I'm allowing only a single file upload: <div class="row"> <div class="col-md-3"> <h3>Select file</h3> <div ng2FileDrop [uploader]="uploader" class="well"> drop here </div> <input type="file" ng2FileSelect [uploader]="uploader" /> </div> <div class="col-md-9" style="margin-bottom: 40px"> <h3>Queue</h3> <p>Length: {{ uploader?.queue?.length }}</p> <table class="table"> <thead> <tr> <th width="50%">name</th> <th>size</th> <th>progress</th> <th>status</th> <th>actions</th> </tr> </thead> <tbody> <tr *ngFor="let item of uploader.queue"> <td><strong>{{ item?.file?.name }}</strong></td> <td *ngIf="uploader.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td> <td *ngIf="uploader.isHTML5"> <div class="progress" style="margin-bottom: 0;"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div> </div> </td> <td class="text-center"> <span *ngIf="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span> <span *ngIf="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span> <span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span> </td> <td nowrap> <button type="button" class="btn btn-success btn-xs" (click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess"> <span class="glyphicon glyphicon-upload"></span> upload </button> <button type="button" class="btn btn-warning btn-xs" (click)="item.cancel()" [disabled]="!item.isUploading"> <span class="glyphicon glyphicon-ban-circle"></span> cancel </button> <button type="button" class="btn btn-danger btn-xs" (click)="item.remove()"> <span class="glyphicon glyphicon-trash"></span> remove </button> </td> </tr> </tbody> </table> <div> <div> Queue progress: <div class="progress"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': uploader.progress + '%' }"></div> </div> </div> <button type="button" class="btn btn-success btn-s" (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length"> <span class="glyphicon glyphicon-upload"></span> upload all </button> <button type="button" class="btn btn-warning btn-s" (click)="uploader.cancelAll()" [disabled]="!uploader.isUploading"> <span class="glyphicon glyphicon-ban-circle"></span> cancel all </button> <button type="button" class="btn btn-danger btn-s" (click)="uploader.clearQueue()" [disabled]="!uploader.queue.length"> <span class="glyphicon glyphicon-trash"></span> remove all </button> </div> </div> </div> Now, when I pick a file to upload and launch the upload, I immediately get this error from the server: Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute If I place a breakpoint in my upload method on server side, it never gets hit. Further, the error message looks deceiving, as I'm using CORS but I'm not allowing any wildcard. Here is my startup code for it (`Startup.cs`, `Configure` method): app.UseCors(builder => builder.WithOrigins("http://localhost:4200") .AllowAnyHeader() .AllowAnyMethod()); The only relevant reference I found googling for this error message is this: https://github.com/filepicker/filepicker-js/issues/42, which was related to a bug. Could you help? Thanks!
Myrmex commented 2016-11-16 17:28:54 +00:00 (Migrated from github.com)

I found a fix at the bottom of this post: https://github.com/valor-software/ng2-file-upload/issues/399 . As a notice to this post readers, FileItem still remains unexported but you can just use an any type. At least, hope this maybe useful to readers interested in an ASP.NET Core sample...

I found a fix at the bottom of this post: https://github.com/valor-software/ng2-file-upload/issues/399 . As a notice to this post readers, `FileItem` still remains unexported but you can just use an `any` type. At least, hope this maybe useful to readers interested in an ASP.NET Core sample...
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/ng2-file-upload#493