ASP.NET core load connection string - heroku

I'm building an ASP.NET Core 2 application. I want to deploy my application to Heroku, however, I need to load the connection string from their environment variable $DATABASE_URL. In my startup.cs I have:
namespace LearningSystems
{
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().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = true;
});
SetupDbContext(services);
}
private void SetupDbContext(IServiceCollection services)
{
var conn = Environment.GetEnvironmentVariable("$DATABASE_URL");
var connectionString = Configuration.GetConnectionString("pmf");
services.AddEntityFrameworkNpgsql()
.AddDbContext<pmf_visualizationsContext>(options => options.UseNpgsql(connectionString));
}
// 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("/Home/Error");
}
app.UseSession();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "Home",
template: "",
defaults: new {controller = "Shell", action = "Index"}
);
});
}
}
}
I want to load a different connection string when I am in a production environment (Heroku).
However, in the method SetupDbContext, I don't know how to find out in which environment I am located. Can anyone tell me what is the correct way to do this?

You can do this by using IHostingEnvironment and Getting the current environment.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments1
[Kindly refer to this link for help]

Related

HttpContext.Session.GetString("string"), Randomly I get Session has not been configured for this application or request exception

Not everytime but some time i get error while i try to get HttpContext.Session.GetString("userid");
I am using .Net core 2.2
Below is my session helper code
public static class SessionHelper
{
private static HttpContext _context = null;
public static void SetCurrentContext(HttpContext context)
{
_context = context;
}
public static string UserID
{
get
{
try
{
return _context.Session.GetString("userid");
}
catch
{
return "";
}
}
set
{
_context.Session.SetString("userid", value);
}
}
}
Below in my middleware configuration in Configure method startup.cs
app.UseSession();
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "sameorigin");
await next();
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseMvcWithDefaultRoute();
Below is my ConfigureServices code of startup.cs
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(60 * 30);
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest;
//options.IOTimeout = TimeSpan.FromSeconds(60 * 30);
});
services.AddMvc()
.AddSessionStateTempDataProvider();

.Net Core 3 Web API with OData $count is not returning a value

I have a working web API which response to a GET request and returns a list of reports. I recently implemented Odata and options such as $filter and $top work when I use $count it appears to be ignored.
I have read through multiple blogs and forms but I have not been able to get it work.
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.ReturnHttpNotAcceptable = true;
o.EnableEndpointRouting = false;
});
// Connection string from environmental variables
var connectionString = Configuration["connectionString:077test"];
// Connection to SQL
services.AddDbContext<ServiceCatalogContext>(option => option.UseSqlServer(connectionString));
// Scope
services.AddScoped<IReportsRepo, ReportsRepo>();
services.AddControllers();
services.AddOData();
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
}
// 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(appBuilder =>
{
appBuilder.Run(async context =>
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync("An unexpected fault happened. Try again later.");
});
});
}
//app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseStatusCodePages();
//app.UseMvc();
//expanding app.UseMvc for oData
app.UseMvc(routeBuilder =>
{
//routeBuilder.EnableDependencyInjection();
routeBuilder.Select()
.OrderBy()
.Filter()
.SkipToken()
.MaxTop(10)
.Expand()
.Count();
routeBuilder.MapODataServiceRoute("api", "api", GetEdmModel());
});
}
private static IEdmModel GetEdmModel()
{
var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<ReportsDto>("reports");
return odataBuilder.GetEdmModel();
}
}
ReportsController.cs
public class ReportsController : ODataController //Controller //ControllerBase
{
private readonly IReportsRepo _reportsRepo;
public ReportsController(IReportsRepo reportsRepo, IMapper mapper)
{
_reportsRepo = reportsRepo ??
throw new ArgumentNullException(nameof(reportsRepo)); //handle if resultsRepo is null
}
[HttpGet]
[EnableQuery()] //enabled OData querying
[ODataRoute("reports")]
public ActionResult<IQueryable<ReportsDto>> GetReports() //updated from IEnumerable to IQueryable when OData was added
{
var results = _reportsRepo.GetReports();
//return Ok(_mapper.Map<IEnumerable<ReportsDto>>(results)); //mapper not working with OData if I change IEnumerable to IQueryable
return Ok(results);
}
}
The API returns results and I can use $filter and $top but I just cannot figure out how to get $count working.
Any help with my specific API would be greatly appreciated!
Use app.UseEndpoints() instead of app.UseMvc(). The equivalent of your UseMvc() would be:
app.UseEndpoints(endpoints =>
{
//endpoints.EnableDependencyInjection();
endpoints.Select()
.OrderBy()
.Filter()
.SkipToken()
.MaxTop(10)
.Expand()
.Count();
endpoints.MapODataRoute("api", "api", GetEdmModel());
});

Asp.Net Core Area routing to Api Controller not working

I have an API controller hosted in an area. However, the routing doesn't seem to be working as my ajax calls keep returning 404's when trying to hit the controller actions. Breakpoints in the controller constructor are never hit.
[Area("WorldBuilder")]
[Route("api/[controller]")]
[ApiController]
public class WorldApiController : ControllerBase
{
IWorldService _worldService;
IUserRepository _userRepository;
public WorldApiController(IWorldService worldService, IUserRepository userRepository)
{
_worldService = worldService;
_userRepository = userRepository;
}
[HttpGet]
public ActionResult<WorldIndexViewModel> RegionSetSearch()
{
string searchTerm = null;
var userId = User.GetUserId();
WorldIndexViewModel model = new WorldIndexViewModel();
IEnumerable<UserModel> users = _userRepository.GetUsers();
UserModel defaultUser = new UserModel(new Microsoft.AspNetCore.Identity.IdentityUser("UNKNOWN"), new List<Claim>());
model.OwnedRegionSets = _worldService.GetOwnedRegionSets(userId, searchTerm);
var editableRegionSets = _worldService.GetEditableRegionSets(userId, searchTerm);
if (editableRegionSets != null)
{
model.EditableRegionSets = editableRegionSets.GroupBy(rs =>
(users.FirstOrDefault(u => u.IdentityUser.Id == rs.OwnerId) ?? defaultUser)
.IdentityUser.UserName)
.Select(g => new RegionSetCollectionModel(g)).ToList();
}
var viewableRegionSets = _worldService.GetViewableRegionSets(userId, searchTerm);
if (viewableRegionSets != null)
{
model.ViewableRegionSets = viewableRegionSets.Where(vrs => vrs.OwnerId != userId).GroupBy(rs =>
(users.FirstOrDefault(u => u.IdentityUser.Id == rs.OwnerId) ?? defaultUser)
.IdentityUser.UserName)
.Select(g => new RegionSetCollectionModel(g)).ToList();
}
return model;
}
}
And my startup.cs file:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(name: "areaRoute",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
}
}
I have tried the following ajax addresses:
localhost:44344/api/WorldApi/RegionSetSearch
localhost:44344/WorldBuilder/api/WorldApi/RegionSetSearch
localhost:44344/api/WorldBuilder/WorldApi/RegionSetSearch
localhost:44344/WorldBuilder/WorldApi/RegionSetSerarch
For the last address I tried, I removed the "api/" from the Route data annotation on the controller.
I'm not sure what I'm doing wrong here. I'm following all of the examples I've found online.
There are two routing type in MVC, conventions routing which is for mvc and route attribute routing which is for web api.
For area which is configured in conventions routings for MVC should not be combine with route attribute. Route attribute will override the default convention routing.
If you prefer attribute routing, you could
[Route("WorldBuilder/api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet("RegionSetSearch")]
public ActionResult<IEnumerable<string>> RegionSetSearch()
{
return new string[] { "value1", "value2" };
}
}
Pay attention to [HttpGet("RegionSetSearch")] which define the action for RegionSetSearch and append a placeholder in the url.
The Reuqest is https://localhost:44389/worldbuilder/api/values/RegionSetSearch
If you prefer conventions routing, you could remove Route and ApiController like
[Area("WorldBuilder")]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> RegionSetSearch()
{
return new string[] { "value1", "value2" };
}
}
With this way, you need to change the UseMvc like
app.UseMvc(routes => {
routes.MapRoute("areaRoute", "{area:exists}/api/{controller}/{action}/{id?}");
});
And the request is https://localhost:44389/worldbuilder/api/values/RegionSetSearch

JWT Token from postman never hits the controller with Authorize attribute

Trying to understand how JWT works for asp.net core application. I use a ASP.NET MVC Core application template.
My StartUp.cs cotains configuration for JWT Token :
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
//options.EventsType = typeof(AuthenticateCustomEvent);
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
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.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
And my login controller contains code to return token on successfull validation of the user this returns a token as shown below
Once i recieve the token i make a call to a controller with [Authorize] attribute applied to it with the bearer token from PostMan the controller never gets hit ? Am i missing something ?
My below solution is bit different, but this solution will help you to deal with custom auth implementation, you can implement a different type of auth for the different type of users. You require to create a class AuthorizationRequiredAttribute under your API project, this class will inherit ActionFilterAttribute class to filter each API request. you can filter all HTTP methods (GET, POST, PUT, DELETE...etc), and can implement your own authorization logic for specific HTTP method.
ActionFilterAttribute.cs
using BillSyatemCore.Common.Constants;
using BillSyatemCore.Services.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
namespace BillSyatemCore.Handlers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class AuthorizationRequiredAttribute : ActionFilterAttribute
{
private IConfiguration _config;
public AuthorizationRequiredAttribute(IConfiguration config)
{
_config = config;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
try
{
if (context.HttpContext.Request.Headers.ContainsKey(Constants.HttpHeaders.Token))
{
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(context.HttpContext.Request.Headers[Constants.HttpHeaders.Token])
as JwtSecurityToken;
var expireDate = Convert.ToDateTime(token.Claims.First(claim => claim.Type == Constants.JwtClaims.ExpiresOn).Value);
if (context.HttpContext.Request.Method == WebRequestMethods.Http.Get)
{
if (expireDate < DateTime.Now)
{
context.Result = new UnauthorizedResult();
}
}
else
{
//You may filter post,put,delete etc request here.
}
}
else
{
context.Result = new NotFoundResult();
}
}
catch (Exception ex)
{
context.Result = new BadRequestResult();
}
base.OnActionExecuting(context);
}
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//JWT
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.Build());
});
}
Controller.cs
using BillSyatemCore.Models.Authentication;
using BillSystemCore.Transporter;
using Microsoft.AspNetCore.Mvc;
namespace TestProject.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
public class UserTypeController : Controller
{
private readonly IAuthTransporter _authTransporter;
public UserTypeController(IAuthTransporter authTransporter)
{
_authTransporter = authTransporter;
}
[HttpPost("save"), ServiceFilter(typeof(AuthorizationRequiredAttribute))]
public IActionResult Save([FromBody] UserType userType)
{
return Ok(_authTransporter.UserTypeServices.Save(userType));
}
}
}

how to connect to SQL database in MVC6 ASP.NET 5 with DB first approach without migrations stuff

public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddEntityFramework().AddSqlServer().AddDbContext<ApplicationDbContext>();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
try
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
}
catch { }
}
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentity();
// To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
I have a Model and my start up class in ASP.NET 5 + MVC 6 look like this. I want to connect my already created Database Table to the application.As it is already created, it would not go for migration .. How to do this in MVC6 + ASP.NET 5

Resources