I created a new project from the ASP.NET Core Web API template in Visual Studio, and attempted to add a new controller, but I get a 404 response with this message when trying to GET from it:
Cannot GET /navigation
My program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
The working controller that came with the template:
using Microsoft.AspNetCore.Mvc;
namespace Test.API.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
My new controller:
using Microsoft.AspNetCore.Mvc;
using Test.API.Navigation;
namespace Test.API.Controllers
{
[ApiController]
[Route("[controller]")]
public class NavigationController : ControllerBase
{
public NavigationController()
{
}
[HttpGet(Name = "GetNavigation")]
public IEnumerable<NavigationItem> Get()
{
return new List<NavigationItem>
{
new NavigationItem()
{
Title = "Home",
Url = "test",
AltText = "Home page"
}
};
}
}
}
How can I get this controller to work? The project seems to have no extra config files that are relevant, just launchsettings.json and applicationsettings.json.
I tried changing the name of the WeatherForecastController, which led to it returning the same response, so it feels like it is hardcoded somehow.
Did you update your proxy.conf.js file? Change the context to /navigation. The target is your target you have now.
const PROXY_CONFIG = [
{
context: [
"/navigation",
],
target: "https://localhost:7105",
secure: false
}
]
module.exports = PROXY_CONFIG;
Related
I am new to IdentityServer4. I have created a IdentityServer4 client, a scope at the IdentityServer4 running at https://localhost:44311/. I secured a sample Weather API using IdentityServer4. When I run Program.cs, I receive an Authorization token. I set this token using client.SetBearerToken(tokenResponse.AccessToken); but when I send GET request to API using await client.GetAsync($"https://localhost:44315/weatherforecast");, I receive 401 Unauthorized or 403 Forbidden. What am I missing? Here is code:
Startup.cs
namespace weatherapi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication("Bearer", options =>
{
options.ApiName = "weatherapi";
options.Authority = "https://localhost:44311/";
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
WeatherForecastController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace weatherapi.Controllers
{
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
Program.cs
using IdentityModel.Client;
using System.Text;
await SampleWeather();
//await SampleAdminApi();
async Task SampleWeather()
{
using var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = "https://localhost:44311/connect/token",
ClientId = "weatherapi",
ClientSecret = "weatherapi",
Scope = "weatherapi_scope",
GrantType = "client_credentials"
});
if (tokenResponse.IsError)
{
throw new Exception("Unable to get token", tokenResponse.Exception);
}
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync($"https://localhost:44315/weatherforecast");
var content = await response.Content.ReadAsStringAsync();
Console.ReadLine();
}
As it seems to be a plain API, I would replace AddIdentityServerAuthentication with AddJwtBearer (see this page)
I would also set this flag to true inside AddJWtBearer
opt.IncludeErrorDetails = true;
Because then you would get some more details in the WWW-Authenticate header that you get back when you get a 401 error, like this:
HTTP/1.1 401 Unauthorized
Date: Sun, 02 Aug 2020 11:19:06 GMT
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
I want to try to filter data based on API key authentication using NET CORE where the key is stored in the header. each key has its own data. is there a reference that can help me with that?
for an example like this
sorry if my question is difficult to understand. thank you very much good luck always
You can use Request.Headers["ApiKey"] to get value of "ApiKey" header,
using that value do your filter logic
Below is work demo, you can refer to it.
Using the Custom Attributes, name the Attribute as ApiKeyAttribute.We will be using this attribute to decorate the controller so that any request that is routed to the attributed controller will be redirected to ApiKeyAttribute.
1.ApiKeyAttribute.cs :
public class ApiKeyAttribute : Attribute, IAsyncActionFilter
{
private const string APIKEYNAME = "ApiKey";
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.HttpContext.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
{
context.Result = new ContentResult()
{
StatusCode = 401,
Content = "Api Key was not provided"
};
return;
}
var appSettings = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
var apiKey = appSettings.GetValue<string>(APIKEYNAME);
if (!apiKey.Equals(extractedApiKey))
{
context.Result = new ContentResult()
{
StatusCode = 401,
Content = "Api Key is not valid"
};
return;
}
await next();
}
}
2.Add the API Key inside the appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ApiKey": "aaaaaaaaaaaaa"
}
Add [ApiKey] to WeatherForecastController
[ApiKey]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
Result:
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Use Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Replace the default controller activator
//builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, CustomControllerActivator>());
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
CustomControllerActivator.cs
public class CustomControllerActivator : IControllerActivator
{
public object Create(ControllerContext context)
{
// `serviceProvider` the object is `Autofac.Extensions.DependencyInjection.AutofacServiceProvider` type
var serviceProvider = context.HttpContext.RequestServices;
...
}
public void Release(ControllerContext context, object controller)
{
...
}
}
WeatherForecastController.cs
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
private readonly IServiceScope serviceProvider;
public WeatherForecastController(ILogger<WeatherForecastController> logger,IServiceProvider serviceProvider)
{
_logger = logger;
//`serviceProvider` the object is `Autofac.Extensions.DependencyInjection.AutofacServiceProvider` type
serviceProvider = serviceProvider;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
I checked the source code and didn't see you replace 'IControllerActivator' or 'IControllerActivatorProvider', so I don't quite understand how you operate the activation of the controller
I don't understand how you replace the object of 'HttpContext.RequestServices' with the type of 'Autofac.Extensions.DependencyInjection.AutofacServiceProvider'
Thank you very much for asking for relief
I would like to have second controller in my asp.net WebApi, but when i add it it not works... First Controller works OK
i have 404 not found in my browser
not any errors while run
whats wrong?
namespace testing.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering",
"Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
and the second is below
{
[Route("[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public int Get()
{
return 100050;
}
}
}
Can some one tell me whats wrong?
You're using the attribute [Route("[controller]")] on your controller class. The string [controller] means "the name of the class, without the actual WORD "Controller".
This means, the name of the controller is "Values" (or "WeatherForecast" for the previous controller).
So, the final url route you want is /Values, not /ValuesController.
You can read more about how this works on the MS Docs page (the whole page has a lot of good information, not just that section).
I'm trying to secure a .Net Core 5 API, with Auth0.
The API keep returning me "401 Unauthorized".
I'm testing the API with Postman Windows App.
I'm messing with the default API template WeatherForecast from Visual Studio 2019.
Calling the public method/EndPoint works fine (http://localhost:20741/WeatherForecast/public).
I'm requesting a token with Postman, which I supply to the GET Request as a Bearer Token.
But When I call the private endpoint (http://localhost:20741/WeatherForecast/private)
I keep getting the 401 error.
I've downloaded the sample .Net Core 3.0 project from Auth0 web site and private or public endpoints work fine. I'm using the same Audience and Authority on both project.
I think it has something to do the .Net Core 5 config.
namespace AuthWebApplication1
{
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using WebAPIApplication;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "AuthWebApplication1", Version = "v1" });
});
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder
.WithOrigins("http://localhost:3000", "http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
string domain = $"https://dev-***2b.us.auth0.com/";
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = "https://localhost:44349/";
});
services.AddAuthorization(options =>
{
options.AddPolicy("read:messages", policy => policy.Requirements.Add(new HasScopeRequirement("read:messages", domain)));
});
// register the scope authorization handler
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AuthWebApplication1 v1"));
}
app.UseRouting();
app.UseCors("AllowSpecificOrigin");
app.UseStaticFiles();
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Controller
namespace AuthWebApplication1.Controllers
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
[HttpGet]
[Route("public")]
public IActionResult Public()
{
return Ok(new
{
Message = "Hello from a public endpoint! You don't need to be authenticated to see this."
});
}
[HttpGet]
[Route("private")]
[Authorize]
public IActionResult Private()
{
return Ok(new
{
Message = "Hello from a private endpoint! You need to be authenticated to see this."
});
}
[HttpGet]
[Route("private-scoped")]
[Authorize("read:messages")]
public IActionResult Scoped()
{
return Ok(new
{
Message = "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this."
});
}
[HttpGet("claims")]
public IActionResult Claims()
{
return Ok(User.Claims.Select(c =>
new
{
c.Type,
c.Value
}));
}
}
}
I had to remove
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// More Code ..
//*************************
// replace this
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//*************************
//*************************
// with this
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//*************************
// some more code
}
public void ConfigureServices(IServiceCollection services)
{
// some code
//*************************
// add this
services.AddMvc(x => x.EnableEndpointRouting = false);
//*************************
// some more code
}