0

I am making an Ajax call to a Web Core API server using localhost on my development machine. This is the code that makes the call;

addDataToDatabase = function (callback, errorCallback, url, data) {
    $.ajax({
        async: true,
        url: url,
        contentType: "application/json",
        dataType: "text",
        data: data,
        type: "POST",
        xhrFields: { withCredentials: true }
    })
        .done(function (data) {
            callback(data);
        })
        .fail(function (data) {
            errorCallback(data);
        });

However I get a 401 unauthorised user exception. In my startup Configure method I set up CORS as follows

    var url = Configuration["originUrl"];
    app.UseCors(
        options => options.WithOrigins(url).AllowAnyHeader().AllowAnyMethod().AllowCredentials()
    ); 

In my appSettings file I set the orginUrl:

"originUrl": "http://localhost:12345"

Which all works for GET type calls but not POST. What do I need to fix?

EDIT - upon request this is the full startup code;

using Microsoft.AspNetCore.Mvc.ApplicationParts;

namespace Properties.API
{
    using Domain;
    using Domain.Interfaces;
    using Domain.Repo;
    using EF6;
    using Helper;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using NLog;
    using NLog.Config;
    using NLog.Extensions.Logging;
    using NLog.Web;
    using Sir.EF6;
    using Sir.EF6.Interfaces;
    using Sir.EF6.Repo;

    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appSettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        public static IConfigurationRoot Configuration;

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            var manager = new ApplicationPartManager();
            manager.ApplicationParts.Add(new AssemblyPart(typeof(Startup).Assembly));
            services.AddSingleton(manager);
            services.AddCors();
            services.AddMvcCore().AddJsonFormatters();
            services.Configure<IISOptions>(options => new IISOptions
            {
                AutomaticAuthentication = true,
                ForwardClientCertificate = false,
                ForwardWindowsAuthentication = false
            });

            var connectionStringMSurveyV2 = Configuration.GetConnectionString("MSurveyV2Db");
            services.AddScoped<MSurveyV2Db>(_ => new MSurveyV2Db(connectionStringMSurveyV2));
            var connectionStringSir = Configuration.GetConnectionString("SirDb");
            services.AddScoped<SirDb>(_ => new SirDb(connectionStringSir));
            services.AddScoped<IPropertiesRepo, PropertiesRepo>();
            services.AddScoped<ISirUoW, SirUoW>();
            services.AddScoped<IPropertyUoW, PropertyUoW>();
            services.AddScoped<Services.IMailService, Services.MailService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();
            loggerFactory.AddNLog();
            LogManager.Configuration = new XmlLoggingConfiguration(@"nlog.config");
            var connectionString = Configuration.GetConnectionString("SirNLogDb");
            LogManager.Configuration.Variables["SirNLogDb"] = connectionString;
            app.AddNLogWeb();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler();
            }

            var url = Configuration["originUrl"];
            app.UseCors(
                options => options.WithOrigins(url).AllowAnyHeader().AllowAnyMethod().AllowCredentials()
            );

            app.UseMvc();
            AutomapperInit.Set();
            var logger = LogManager.GetCurrentClassLogger();
            logger.Info("Started Properties Web API");
            logger.Info($"Origin url = {url}");
        }
    }
}

This is a POST method that provokes the 401 exception;

[HttpPost("add")]
public async Task<IActionResult> Add([FromBody] InspectionVisitInputDto inspectionVisitDto)
{
    this.NLogger.Info("api/inspectionVisit/add".ToPrefix());
    if (inspectionVisitDto == null)
    {
        NLogger.Error("No data sent");
        return BadRequest(ModelState);
    }

    if (inspectionVisitDto.InspectionId < 1)
    {
        NLogger.Error($"Invalid InspectionId < 1 (actual value is {inspectionVisitDto.InspectionId}");
        return BadRequest(ModelState);
    }

    var inspectionVisit = Mapper.Map<InspectionVisit>(inspectionVisitDto);
    var dateOfVisit = inspectionVisit.DateOfVisit.Date;
    try
    {
        var existingInspectionVisit = this.SirUoW.InspectionVisit.GetItem(
            x => x.InspectionId == inspectionVisit.InspectionId &&
                 DbFunctions.TruncateTime(x.DateOfVisit) == dateOfVisit &&
                 x.ContractSubcontractorId == inspectionVisit.ContractSubcontractorId &&
                 x.SelectedInspectorUsername == inspectionVisit.SelectedInspectorUsername &&
                 x.WorkPhaseId == inspectionVisit.WorkPhaseId);
        if (existingInspectionVisit?.InspectionVisitId > 0)
        {
            NLogger.Info($"Inspection Visit Id = {existingInspectionVisit.InspectionVisitId} already exists.");
            return Ok(existingInspectionVisit.InspectionVisitId);
        }
    }
    catch (Exception e)
    {
        var message = "Cannot get inspection visit";
        await ReportException(e, message);
        var status = OperationStatus.CreateFromException(message, e);
        return BadRequest(status);
    }

    NLogger.Info("Add New Inspection Visit");
    try
    {
        this.SirUoW.InspectionVisit.Add(inspectionVisit);
        await SirUoW.LoggedInSaveChangesAsync(this.LoggedInUser);
        NLogger.Info("New Inspection Visit Saved, id = " + inspectionVisit.InspectionVisitId);

        if (inspectionVisit.ContractSubcontractorId != null)
        {
            await SaveContractSubcontractorIdForInspection(inspectionVisitDto);
        }

        return Ok(inspectionVisit.InspectionVisitId);
    }
    catch (Exception e)
    {
        var message = "Cannot save " + inspectionVisitDto;
        await ReportException(e, message);
        var status = OperationStatus.CreateFromException(message, e);
        return BadRequest(status);
    }
}

And this is a GET method that works OK

[HttpGet("getall")]
public async Task<IActionResult> GetContracts()
{
    this.NLogger.Info("api/contracts/getall".ToPrefix());
    try
    {
        var contracts = this.SirUoW.ViewAllContracts.GetList(x => x.IsMainDivision && x.EndDate == null).OrderBy(x => x.FullContractNo).ToList();
        return Ok(contracts);
    }
    catch (Exception e)
    {
        var message = "Error getting contracts";
        await ReportException(e, message);
        var status = OperationStatus.CreateFromException(message, e);
        return BadRequest(status);
    }
}
arame3333
  • 9,887
  • 26
  • 122
  • 205

1 Answers1

0

Your scenario is combining IIS Windows Authenticaiton and CORS, since preflight request OPTIONS would not contains any security header, it will hit the error 401.

Try workaround below:

  1. Enable anonymousAuthentication in appsettings.json

    {
      "iisSettings": {
      "windowsAuthentication": true,
     "anonymousAuthentication": true
     }
    }
    
  2. For enable authentication, try Filter to disable anonymous access.

      services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
        });   
    
Edward
  • 28,296
  • 11
  • 76
  • 121
  • That get me a "Response status code does not indicate success: 500 (Internal Server Error)." for a Get request – arame3333 Aug 24 '18 at 15:38
  • @arame3333 If you put breakpoint on the `Get` and `Post` method, will them be hit? What is the detail error message? – Edward Aug 25 '18 at 01:48