i am trying to make an angular application that takes an api made from asp.net core but while making the api , it didn't work and appear as planned and didn't know where was the problem...
I made an asp.net core web app.
This is the student.cs file made in the model folder
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebAPI101.Model
{
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public bool Pass { get; set; }
}
}
This is the studentmanager also in model folder
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebAPI101.Model
{
public class StudentManager
{
public List<Student> GetResults()
{
List<Student> oList = new List<Student>();
var r = new Random();
for(int i = 0; i < 10; i++)
{
var x = new Student();
x.ID = i;
x.Name = String.Format("Name{0}", i, ToString());
x.Pass = (r.Next() % 2 == 0);
oList.Add(x);
}
return oList;
}
}
}
This is startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebAPI101
{
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.AddRazorPages();
}
// 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();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
This is the code that should return aaaaa
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebAPI101.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
[Route("mariam")]
public string test()
{
return "aaaaa";
}
}
}
I tried to launch the code and it didn't work as planned o the link:https://localhost:5001/api/Student/mariam
While running the application on the previous link it appeared like that:
browser status after running
This is the lanchsettings.json:
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:40854",
"sslPort": 44361
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebAPI101": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "api/Student/mariam",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
The configuration is missing AddControllers and MapControllerRoute calls. Checkout the example below:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WebAPI101
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = 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)
{
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.UseRouting();
app.UseDefaultFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
"default",
"api/{controller}/{action=Index}");
});
}
}
}
Related
I am logging with Serilog and have a (text) file sink with an output template and a console logger.
The output template of the text file logger:
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff}|{Level:u3}|{SourceContext}|{Message}{NewLine:1}{Exception:1}"
The output template of the console logger:
"outputTemplate": "{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext} {Message:lj}{NewLine:1}{Exception:1}"
Now we try (for the sake of easiness) to log some class data in JSON format. We want it as JSON beacuse we try to extract the data later from the log files.
The sample logging code is easy enough:
protected void DoTestLogs()
{
Logger.LogInformation("HERE IT IS");
Logger.LogInformation("HERE IT IS {#Foo}", new { Foo = "bar" });
int x = 1;
string y = "2";
Logger.LogInformation("HERE IS ANOTHER ONE {#Other}", new { x, y });
var data = new My.Data
{
Name = "my data",
IsValid = true,
Value = 27.82859584
};
Logger.LogInformation("HERE IS DATA {#Data}", data);
}
The console output looks valid as expected (except the serialzed type of data which I could ignore)
08:09:38.880 INF My.Test HERE IT IS
08:09:38.884 INF My.Test HERE IT IS {"Foo": "bar"}
08:09:38.888 INF My.Test HERE IS ANOTHER ONE {"x": 1, "y": "2"}
08:09:38.889 INF My.Test HERE IS DATA {"Name": "my data", "Value": 27.82859584, "IsValid": true, "$type": "Data"}
But the json in the text file log seems to be "randomly" invalid. I can't explain why quotes are there or not
2023-01-27 08:09:38.880|INF|My.Test|HERE IT IS
2023-01-27 08:09:38.884|INF|My.Test|HERE IT IS { Foo: "bar" }
2023-01-27 08:09:38.888|INF|My.Test|HERE IS ANOTHER ONE { x: 1, y: "2" }
2023-01-27 08:09:38.889|INF|My.Test|HERE IS DATA Data { Name: "my data", Value: 27.82859584, IsValid: True }
Does anyone have a hint how I could trick the file sink to put quotes around the names like Foo, x, y ... as the console sink does?
In case anyone want's to reproduce it and doesn't have a serilog setup at hand: here is the setup.
First create a new project from the "ASP.NET Core Web API" template. Then delete the WeatherForecast.cs and the WeatherForecastController.
Add the Serilog nuget to the cs.proj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
</ItemGroup>
</Project>
Configure Serilog via the appsettings.json
{
"AllowedHosts": "*",
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext} {Message:lj}{NewLine:1}{Exception:1}"
}
},
{
"Name": "File",
"Args": {
"path": "logs/log.log",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff}|{Level:u3}|{SourceContext}|{Message}{NewLine:1}{Exception:1}"
}
}
]
}
}
Integrate Serilog in the Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Stackoverflow.Question75255058
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog
(
(context, services, configuration) =>
configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
No need for changes in the StartUp.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Stackoverflow.Question75255058
{
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 = "Stackoverflow.Question75255058", Version = "v1" });
});
}
// 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", "Stackoverflow.Question75255058 v1"));
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Create an new OutputController and the My.Data class under controllers
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Stackoverflow.Question75255058.Controllers
{
[ApiController]
[Route("[controller]")]
public class LogOutputController : ControllerBase
{
public readonly ILogger<LogOutputController> Logger;
public LogOutputController(ILogger<LogOutputController> logger)
{
Logger = logger;
}
[HttpGet]
public IActionResult Get()
{
Logger.LogInformation("HERE IT IS");
Logger.LogInformation("HERE IT IS {#Foo}", new { Foo = "bar" });
int x = 1;
string y = "2";
Logger.LogInformation("HERE IS ANOTHER ONE {#Other}", new { x, y });
var data = new My.Data
{
Name = "my data",
IsValid = true,
Value = 27.82859584
};
Logger.LogInformation("HERE IS DATA {#Data}", data);
return NoContent();
}
}
}
namespace My
{
public class Data
{
public string Name { get; set; }
public bool IsValid { get; set; }
public double Value { get; set; }
}
}
Start the application and execute the controller action provided by Swagger.
The difference is in your outputTemplate - {Message:lj} produces literal strings and JSON objects, while the default {Message} produces quoted strings and the earlier C#-like object format.
Using the same output template with each sink should produce the same result.
This line is causing an error in my project:
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
Error:
System.ArgumentNullException: 'Value cannot be null. (Parameter 'connectionString')'
I have read through a bunch of similar problems online and I cannot find a change that works. It seems that the parameter inside UseSqlServer() is returning null. This leads me to believe that I have a configuration mistake.
Here is my code for my Startup and json files:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
namespace ContosoUniversity
{
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.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
{
"ConnectionStrings": {
"DefaultConnection": "Server=(***;Database=***;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"AllowedHosts": "*"
}
Any assistance is appreciated. Thanks.
You are trying to get a connection string called SchoolContext but you only have one in the config called DefaultConnection. So either fix your code:
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
Or fix your config:
{
"ConnectionStrings": {
"SchoolContext": "Server=....."
},
// etc...
I cant create a migration in asp.net core because i keep getting cant construct db context and im using 2 contexts. Ive tried everything to try and fix this but I still cant create migrations because it says it has an error constructing db context and all my code is fine and I cant find out why this wont work. Can someone please help, Thanks.
ApplicationDbContext
using GraphQlApiFullStack.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace GraphQlApiFullStack.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
}
}
IdentityAppDbContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GraphQlApiFullStack.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace GraphQlApiFullStack.Data
{
public class IdentityAppDbContext : IdentityDbContext<AppUser>
{
public IdentityAppDbContext(DbContextOptions<IdentityDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
}
}
identityServicesExtension.cs
using System.Text;
using GraphQlApiFullStack.Data;
using GraphQlApiFullStack.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace GraphQlApiFullStack.Extensions
{
public static class IdentityServiceExtensions
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
{
var builder = services.AddIdentityCore<AppUser>();
builder = new IdentityBuilder(builder.UserType, builder.Services);
builder.AddEntityFrameworkStores<IdentityAppDbContext>();
builder.AddSignInManager<SignInManager<AppUser>>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Token:Key"])),
ValidIssuer = config["Token:Issuer"],
ValidateIssuer = true,
ValidateAudience = false
};
});
return services;
}
}
}
startup.cs
using GraphQL.Server.Ui.Voyager;
using GraphQlApiFullStack.Data;
using GraphQlApiFullStack.Extensions;
using GraphQlApiFullStack.GraphQL;
using GraphQlApiFullStack.Services;
using GraphQlApiFullStack.Services.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace GraphQlApiFullStack
{
public class Startup
{
private readonly IConfiguration _configuration;
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 = "GraphQlApiFullStack", Version = "v1"});
});
// We use a pooled Db Context Factory here to avoid issues with graphql concurrency
services.AddPooledDbContextFactory<ApplicationDbContext>(builder =>
{
builder.UseSqlServer(_configuration.GetConnectionString("defaultConnection"));
});
// Identity will use just a normal db context since we wont be making multiple concurrent queries to it.
services.AddDbContext<IdentityAppDbContext>(options =>
{
options.UseSqlServer(_configuration.GetConnectionString("identityConnection"));
});
services.AddHttpContextAccessor();
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddFiltering()
.AddSorting()
.AddProjections();
services.AddScoped<ITokenService, TokenService>();
//services.AddIdentityServices(_configuration);
}
// 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", "GraphQlApiFullStack v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapGraphQL();
});
app.UseGraphQLVoyager(new VoyagerOptions()
{
GraphQLEndPoint = "/graphql"
});
}
}
}
I get an error of "Ambiguity between 'Startup.Configuration' and 'Startup.Configuration'" on my startup class. I don't know what I have done to cause this issue. All I did was create a DBContext class and this error occured. Please see my code below:
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using IssueTracker.Data;
namespace IssueTracker
{
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Enabling CORS
services.AddCors(options =>
{
options.AddPolicy("EnableCORS", builder =>
{
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().Build();
});
});
// Connect to the Database
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.getConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors("EnableCORS");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
ApplicationDbContext
using IssueTracker.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace IssueTracker.Data
{
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) {}
// Creating the roles
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IdentityRole>().HasData(
new { Id = "1", name="Admin", NoralizeName = "ADMIN" },
new { Id = "1", name="Customer", NoralizeName = "CUSTOMER" },
new { Id = "1", name="Moderator", NoralizeName = "MODERATOR" }
);
}
public DbSet<ProductModel> Products { get; set; }
}
}
Could you please tell me what is wrong here? Thank you.
I had the same error and simply closing and reopening my IDE (Visual Studio Code) solved it. I hope it helps
One reason for this error might be that you have accidently made a copy of Startup.cs file.
I coppied solution file for backup purposes then I got that error. When I delete the backup file problem solved.
I'm working on building an API for handling identity stuff in .NET Core, but every time I try and make a call I get a 404.
There didn't seem to be anything clear when I looked around for an answer, since the code posted seems quite minimal. Here's all the stuff I think is pertinent.
The Controller:
using Common.Extensions;
using Identity.Database.Contexts.Models;
using Identity.WebApi.Models;
using Identity.WebApi.Models.Tokens;
using Identity.WebApi.Services.Access;
using Identity.WebApi.Services.Accounts;
using Identity.WebApi.Services.Tokens;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Controller = Microsoft.AspNetCore.Mvc.Controller;
using Get = Microsoft.AspNetCore.Mvc.HttpGetAttribute;
using Post = Microsoft.AspNetCore.Mvc.HttpPostAttribute;
using Route = Microsoft.AspNetCore.Mvc.RouteAttribute;
namespace Identity.WebApi.Controllers
{
[Route("api/[controller]")]
public class IdentityController : Controller
{
private readonly IApplicationUserService _userService;
private readonly IAccessService _accessService;
private readonly ITokenService _tokenService;
private readonly SignInManager<ApplicationUser> _signInManager;
public IdentityController(IApplicationUserService userService, IAccessService accessService, ITokenService tokenService, SignInManager<ApplicationUser> signInManager)
{
_userService = userService;
_accessService = accessService;
_tokenService = tokenService;
_signInManager = signInManager;
}
[Get]
[AllowAnonymous]
public string Index()
{
return new Dictionary<string,string>
{
{ "status", "live" }
}.Serialize();
}
[Post]
[Route("create")]
[AllowAnonymous]
public Task<ISet<IdentityResult>> Create(string user)
{
var decodedUser = DecodeUser(user);
var applicationUser = new ApplicationUser(new User
{
Id = Guid.NewGuid(),
Name = decodedUser.Username,
LastActive = DateTime.UtcNow
});
return _userService.Add(applicationUser, decodedUser.Password);
}
private (string Username, string Password) DecodeUser(string encodedUser)
{
var decodedUser = encodedUser.DecodeFrom64().Split(':');
return (Username: decodedUser[0], Password: decodedUser[1]);
}
private async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
=> await _signInManager.UserManager.CheckPasswordAsync(user, password);
}
}
The Startup:
using Identity.Database.Contexts;
using Identity.Database.Contexts.Access;
using Identity.Database.Contexts.Extensions;
using Identity.Database.Contexts.Models;
using Identity.WebApi.Models;
using Identity.WebApi.Services.Access;
using Identity.WebApi.Services.Accounts;
using Identity.WebApi.Services.Certs;
using Identity.WebApi.Services.Tokens;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Runtime.CompilerServices;
namespace Identity.WebApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(new CertService(Configuration) as ICertService)
.AddTransient<IApplicationUserService, ApplicationUserService>()
.AddTransient<IApplicationRoleService, ApplicationRoleService>()
.AddTransient<IAccessService, AccessService>()
.AddTransient<ICertService, CertService>()
.AddTransient<ITokenService, TokenService>()
.AddTransient<ICrudDao<AppDbContext, Role>, RoleDao>()
.AddIdentities<ApplicationUser, ApplicationRole>()
.AddScoped<UserManager<ApplicationUser>, UserManager<ApplicationUser>>()
.AddScoped<SignInManager<ApplicationUser>, SignInManager<ApplicationUser>>()
.AddScoped<RoleManager<ApplicationRole>, RoleManager<ApplicationRole>>()
.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (c, n) =>
{
await n();
if (c.Response.StatusCode == 404)
{
c.Request.Path = "/identity";
await n();
}
});
app.UseStaticFiles();
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc(r => { r.MapRoute(name: "default", template: "{controller=identity}/{action=Index}"); });
}
}
}
The launch settings:
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:55048/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/identity/index",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebApplication1": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/identity/index",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:55048/"
}
}
}
At the top of your controller you have:
[Route("api/[controller]")]
public class IdentityController : Controller
Which means that if your route starts with api/ only then it will match the controller. Also, your Index action doesn't have any extra routing attributes on it, so its looking for api/identity only. However, your launch settings don't match that part, and since you don't have any other routes matching it, you get a 404.
The default route in app.UseMvc won't work for this reason.
Simple fix: change launchUrl to just api/identity in your launch settings... and then follow #Nkosi's answer
If using attribute routing then there is no api/identity/index as [HttpGet] or Get in your example with a route prefix, is the same as
[Get] //<-- Matches GET api/identity
[AllowAnonymous]
public IActionResult Index() {
var result = new Dictionary<string,string>
{
{ "status", "live" }
}.Serialize();
return Ok(result);
}
And since this appears to be a Web API that is not expected to return a View then the Http{Verb} attribute with a route template would be the option to use for routing
When building a REST API, it's rare that you will want to use [Route(...)] on an action method. It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
[Post("create")] //<-- Matches POST api/identity/create
[AllowAnonymous]
public async Task<IActionResult> Create(string user) {
var decodedUser = DecodeUser(user);
var applicationUser = new ApplicationUser(new User
{
Id = Guid.NewGuid(),
Name = decodedUser.Username,
LastActive = DateTime.UtcNow
});
ISet<IdentityResult> result = await _userService.Add(applicationUser, decodedUser.Password);
return Ok(result);
}
Reference Routing to Controller Actions