New File Upload ASP.NET Core API method (clone of user upload) not working - 401 unauthorized error
This is v6.9 angular/asp.net core template.
- I have a requirement to upload/import a spreadsheet and load the data into the database.
- The pattern follows nicely the same pattern implemented in your framework to upload users.
- I cloned the modules related to user upload from Excel and transformed into my custom modules:
**asp.net core side solution:
.Application project:
- Cloned and modified all related class files under /Authorization/Users/Importing to /CashSheets/Importing a. Dto/ImportUserDto.cs to Dto/ImportCashSheetTransactionDto.cs b. /IInvalidUserExporter.cs to /IInvalidCashSheetTransactionExporter.cs c. /ImportUsersToExcelJob.cs to /ImportCashSheetTransactionsFromExcelJob.cs d. /InvalidUserExporter.cs to /InvalidCashSheetTransactionExporter.cs e. /UserListExcelDataReader.cs to /CashSheetTransactionListExcelDataReader.cs f. /IUserListExcelDataReader.cs to /ICashSheetTransactionListExcelDataReader.cs
.Shared project:
- Cloned and modified /Authorization/Users/Dto/ImportUsersFromExcelJobArgs.cs to /CashSheets/Dto/ImportCashSheetTransactionsFromExcelJobArgs.cs
.Web.Core project:
- Cloned and modified /Controllers/UsersControllerBase.cs to /Controllers/CashSheetsControllerBase.cs
.Web.Host project:
- Cloned and modified /Controllers/UsersController.cs to /Controllers/CashSheetsController.cs
**CashSheetsControllerBase.cs:
using System; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Abp.IO.Extensions; using Abp.UI; using Abp.Web.Models; using SchlarmanConsulting.OneTAFUnify.Authorization.Users.Dto; using SchlarmanConsulting.OneTAFUnify.Storage; using Abp.BackgroundJobs; using SchlarmanConsulting.OneTAFUnify.Authorization; using Abp.AspNetCore.Mvc.Authorization; using Abp.Authorization; using Abp.Runtime.Session; using GraphQL; using Microsoft.AspNetCore.Authorization; using SchlarmanConsulting.OneTAFUnify.CashSheets; using SchlarmanConsulting.OneTAFUnify.CashSheets.Dto; using SchlarmanConsulting.OneTAFUnify.CashSheets.Importing;
namespace SchlarmanConsulting.OneTAFUnify.Web.Controllers { public abstract class CashSheetsControllerBase : OneTAFUnifyControllerBase { protected readonly IBinaryObjectManager BinaryObjectManager; protected readonly IBackgroundJobManager BackgroundJobManager;
protected CashSheetsControllerBase(
IBinaryObjectManager binaryObjectManager,
IBackgroundJobManager backgroundJobManager)
{
BinaryObjectManager = binaryObjectManager;
BackgroundJobManager = backgroundJobManager;
}
//[AllowAnonymous] //currently only way this works, but then can't get abp user object needed for transactions :\
//[AbpAllowAnonymous] //this doesn't work either
[HttpPost]
[AbpAuthorize,
AbpMvcAuthorize,
AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets),
AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create),
AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)]
public async Task<JsonResult> ImportFromExcel()
{
try
{
var file = Request.Form.Files.First();
//TODO: parse file name for batch number and org, etc.
if (file == null)
{
throw new UserFriendlyException(L("File_Empty_Error"));
}
if (file.Length > 1048576 * 100) //100 MB
{
throw new UserFriendlyException(L("File_SizeLimit_Error"));
}
byte[] fileBytes;
using (var stream = file.OpenReadStream())
{
fileBytes = stream.GetAllBytes();
}
var tenantId = AbpSession.TenantId;
var fileObject = new BinaryObject(tenantId, fileBytes);
//set CashSheetBatchID also to assign to args
var dict = Request.Form.ToDictionary(x => x.Key, x => x.Value.ToString());
var cashSheetBatchID = Int32.Parse(dict["CashSheetBatchID"]);
await BinaryObjectManager.SaveAsync(fileObject);
await BackgroundJobManager.EnqueueAsync<ImportCashSheetTransactionsFromExcelJob, ImportCashSheetTransactionsFromExcelJobArgs>(new ImportCashSheetTransactionsFromExcelJobArgs
{
//These fields are required for core operation
//This is where arg values are set
TenantId = tenantId,
BinaryObjectId = fileObject.Id,
User = AbpSession.ToUserIdentifier(), //think this isn't working because entering anonymously
//User = objUser,
//need to also set passed in CashSheetBatchID
CashSheetBatchID = cashSheetBatchID
});
return Json(new AjaxResponse(new { }));
}
catch (UserFriendlyException ex)
{
return Json(new AjaxResponse(new ErrorInfo(ex.Message)));
}
}
}
}
**CashSheetsController.cs:
using Abp.AspNetCore.Mvc.Authorization; using SchlarmanConsulting.OneTAFUnify.Authorization; using SchlarmanConsulting.OneTAFUnify.Storage; using Abp.BackgroundJobs;
namespace SchlarmanConsulting.OneTAFUnify.Web.Controllers { //auth seems to be working fine here [AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create), AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)] public class CashSheetsController : CashSheetsControllerBase { public CashSheetsController(IBinaryObjectManager binaryObjectManager, IBackgroundJobManager backgroundJobManager) : base(binaryObjectManager, backgroundJobManager) { } } }
Angular solution:
Create-or-edit-cashSheetBatch-modal.component.html:
…
<a href="javascript:;" *ngIf="isGranted('Pages.ProcessCashSheets')" class="no-padding">
<span>
<p-fileUpload customUpload="true"
name="ExcelFileUpload"
#ExcelFileUpload
maxFileSize="10000000"
auto="auto"
accept=".csv,.xls,.xlsx"
(uploadHandler)="uploadExcel($event)"
(onError)="onUploadExcelError()"
chooseLabel="{{'ImportFromExcel' | localize}}">
</p-fileUpload>
</span>
</a>
…
Create-or-edit-cashSheetBatch-modal.component.ts:
//upload import { FileUpload } from 'primeng/fileupload'; import { HttpClient } from '@angular/common/http';
…
export class CreateOrEditCashSheetBatchModalComponent extends AppComponentBase implements OnInit{
//CONSTRUCTOR
constructor(
injector: Injector,
private _cashSheetTransactionsServiceProxy: CashSheetTransactionsServiceProxy, //added for local use to load bank filter dropdown
private _cashSheetBatchesServiceProxy: CashSheetBatchesServiceProxy,
public formBuilderStepper:FormBuilder, //stepper visual
private _httpClient: HttpClient, //for file upload
private formBuilderCashSheetAccountSummaries: FormBuilder, //for accountsummaries
public cashSheetAccountSummariesEditService: CashSheetAccountSummariesEditService //for account summaries
) {
super(injector);
this.uploadUrl = AppConsts.remoteServiceBaseUrl + '/CashSheets/ImportFromExcel'; //for file upload
}
…
//IMPORT TAB DECLARATIONS
//file upload
@ViewChild('ExcelFileUpload') excelFileUpload: FileUpload;
//file upload
uploadUrl: string;
…
//IMPORT TAB EVENT HANDLERS
uploadExcel(data: { files: File }): void {
const formData: FormData = new FormData();
const file = data.files[0];
var cashSheetBatchID: string = "";
formData.append('file', file, file.name);
//get cash sheet batch id - this should be created in show
if (this.cashSheetBatch.id == null) {
//do nothing, accept initialized value
} else {
//cast to string
cashSheetBatchID=this.cashSheetBatch.id.toString();
}
//Append Cash Sheet Batch ID to form object
formData.append("CashSheetBatchID", cashSheetBatchID);
//post the request - transfer the Excel file to the API for import/processing
//TODO: get list of errors back from API - this will populate the new "problems" table to replace
//the field in the equivalent LP table
//TODO: Also need to pass in QBBatchSummary start and end date values - remote function will populate
//based on detail data
this._httpClient
.post<any>(this.uploadUrl, formData)
.pipe(finalize(() => this.excelFileUpload.clear()))
.subscribe(response => {
if (response.success) {
this.notify.success(this.l('ImportCashSheetProcessStart'));
} else if (response.error != null) {
this.notify.error(this.l('ImportCashSheetUploadFailed'));
}
});
}
onUploadExcelError(): void {
this.notify.error(this.l('ImportCashSheetUploadFailed'));
}
**So, I have a catch 22:
If I leave the [AllowAnonymous] decorator off CashSheetsControllerBase.ImportFromExcel, then I get a permission error:
Failed to load resource: the server responded with a status of 401 (Unauthorized) [http://localhost:22742/CashSheets/ImportFromExcel] ERROR core.js:15724 HttpErrorResponse {headers: HttpHeaders, status: 401, statusText: "Unauthorized", url: "http://localhost:22742/CashSheets/ImportFromExcel", ok: false, …} core.js:15724
I've tried multiple combinations of [AbpMvcAuthorize] decorator values, and nothing seems to work.
If I set the decorator on CashSheetsControllerBase.ImportFromExcel to [AllowAnonymous], I can use the method without the above permissions error from angular, but the User object is not populated, and I can’t use it further on in processing as I need.
I’ve compared all the modules in the UserUpload process to my cloned modules, and it seems like I’m doing everything the same. I must be missing something. Any thoughts?
9 Answer(s)
-
0
Hi,
I think the original implementation has a bug, that is why you are not able to use similar approach. Can you import users via excel file ?
-
0
Hi,
Yes, I can import users (if the input file contains certain data in the exact right format) and also the httppost method/file upload works without permission error. I think there are some other bugs in the user import (it doesn’t return erroneous records correctly, etc.), but the main thing is I need to get past this permission error. Other thoughts on what to try?
Lastly (but secondary importance), is there a fix for known users import bugs that I can implement without upgrading the template?
-
0
Hi again - could you tell me how the abp auth works User method "ImportFromExcel" (of UsersControllerBase.cs) and not my clone of that class/method (CashSheetsControllerBase.cs)? Seems I'm missing some config or decorator somewhere, but I've tried everything I can think of. As you can see, I've also tried a straight [AbpMvcAuthorize] (which just requires a user logon, correct?) which doesn't work either.
Any help is much appreciated.
Cheers
-
0
Hi @schlarmanp
Sorry for my late response, have you solved this problem ?
Thanks,
-
0
Hi @ismcagdas - no, still have not solved this.
-
0
Hi,
Does your request for uploading a file contains an header like this:
If not, could you add AbpHttpInterceptor as we do in https://github.com/aspnetzero/aspnet-zero-core/blob/dev/angular/src/shared/service-proxies/service-proxy.module.ts#L43 to your custom module ?
-
0
Hi,
The addition of AbpHttpInterceptor references as suggested worked for passing authentication to the upload httppost method. The method can be adorned with [AbpAuthorize] successfullly and once inside the method, I can see the user object is populated. Thank you.
[HttpPost] [AbpMvcAuthorize] //[AbpAuthorize, // AbpMvcAuthorize, // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets), // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create), // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)] public async Task<JsonResult> ImportFromExcel() { try { var file = Request.Form.Files.First(); //TODO: parse file name for batch number and org, etc. if (file == null) { ...
User obj is populated:
?AbpSession.ToUserIdentifier()
{3@1} TenantId: 1 UserId: 3
I just have one follow-up question. Once further along inside the method, when I make a call to an app object to interact with (PowerTools generated) entity objects, if the method is adorned with any [AbpAuthorize] tag, an error "Current user did not login to the application!" is thrown.
CashSheetTransactionAppService.cs:
namespace SchlarmanConsulting.OneTAFUnify.CashSheets { [AbpAuthorize] //if I remove this everything works public class CashSheetTransactionsAppService : OneTAFUnifyAppServiceBase, ICashSheetTransactionsAppService { private readonly IRepository<CashSheetTransaction> _cashSheetTransactionRepository; private readonly ICashSheetTransactionsExcelExporter _cashSheetTransactionsExcelExporter;
public CashSheetTransactionsAppService(IRepository<CashSheetTransaction> cashSheetTransactionRepository, ICashSheetTransactionsExcelExporter cashSheetTransactionsExcelExporter ) { _cashSheetTransactionRepository = cashSheetTransactionRepository; _cashSheetTransactionsExcelExporter = cashSheetTransactionsExcelExporter; }
...
[AbpAuthorize] private async Task Create(CreateOrEditCashSheetTransactionDto input) { var cashSheetTransaction = ObjectMapper.Map<CashSheetTransaction>(input);
await _cashSheetTransactionRepository.InsertAsync(cashSheetTransaction); } ...
When I attempt to call this further along after the httppost method entry,
//Create insertable object var cashSheetTransaction = new CreateOrEditCashSheetTransactionDto { CashSheetBatchID = input.CashSheetBatchID, CashSheetBatchAccountSummaryID = input.CashSheetBatchAccountSummaryID, CostpointProjectCode = input.CostpointProjectCode, CostpointAccountNumber = input.CostpointAccountNumber, AccountAbbreviation = input.AccountAbbreviation, CostpointOrganizationCode = input.CostpointOrganizationCode, TransactionDescription = input.TransactionDescription, TransactionAmountUSD = input.TransactionAmountUSD, QBAccountNumber = input.QBAccountNumber, QBAmount = input.QBAmount, QBDescription = input.QBDescription, QBPayee = input.QBPayee, QBTransactionDate = input.QBTransactionDate, QBTransactionNumber = input.QBTransactionNumber, TransactionState = input.TransactionState, ProblemCodes = input.ProblemCodes, BankAccountID = input.BankAccountID, CashSheetBankAccountDescription = input.CashSheetBankAccountDescription //Id = 0 }; //Create new transaction //await _cashSheetTransactionRepository.InsertAsync(cashSheetTransaction); await _cashSheetTransactionsAppService.CreateOrEdit(cashSheetTransaction);
I receive the error:
?exception {"Current user did not login to the application!"} Data: {System.Collections.ListDictionaryInternal} HResult: -2146233088 HelpLink: null InnerException: null Message: "Current user did not login to the application!"
I don't understand how the credentials could be lost along the way once inside the API method. I also started from scratch and made sure I logged on fresh to eliminate that possibility.
Thoughts?
-
0
Hi @schlarmanp
This is definitely not normal. Are you able to share your project or a reproduction project via email to [email protected] ?
-
0
Was it ever resolved what the problem was here?