I am newby in .NET, I know more about BE development in Vapor. Anyway.
How can I find the available routes in a .NET app?
How can I know which parameters are accepted.
Thia app here does a transformation from DB to XML stored in an FTP, back and forth. Basically only signals are sent to the app, but I do not know how.
This is the file structure:
And I found some "route-like" lines here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ptoolApi.Models.IFS;
using ptoolApi.Services;
namespace ptoolApi
{
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.AddDbContext<ifsContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:IFS"]));
services.AddScoped<ISqliteService, SqliteService>();
services.AddSwaggerGen(swagger =>
{
swagger.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "pTool API",
Version = "v2.1.5",
Description = "Tool to convert PFB plist file to a sqlite DB, and vice versa"
});
});
}
// 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 =>
{
#if DEBUG
c.SwaggerEndpoint("/swagger/v1/swagger.json", "pTool API");
#elif DEVELOPMENT
c.SwaggerEndpoint("/dev/ptooldb/swagger/v1/swagger.json", "pTool API");
#elif STAGE
c.SwaggerEndpoint("/stage/ptooldb/swagger/v1/swagger.json", "pTool API");
#elif RELEASE
c.SwaggerEndpoint("/ptooldb/swagger/v1/swagger.json", "pTool API");
#endif
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Is here /swagger/v1/swagger.json a route? And what is the accepted arguments?
Meantime I tried
http://localhost:50491/swagger
http://localhost:50491/swagger/index.html
send POST with this parameter
{
"xmltodb": false,
"dbtoxml": true,
"airlineID": 2901,
"deploy": false,
"XamarinMigration": false,
"environment": "prod"
}
But all is refused, though I found these line under controller:
[HttpGet]
public RedirectResult Get()
{
return RedirectPermanent("/swagger/index.html");
}
[HttpPost]
public async Task<ActionResult> Post(PToolInput input)
{
try
{
Globals.WriteToLog("Start the connection");
The "Routes" are in the Controllers folder. There are classes called Controllers there which have methods, that can represent an action (a url). Swagger is a tool to scaffold the api and create a OpenApi documentation for it.
Swagger is a tool (language?) used to describe/document APIs. I think all .NET/.NET Core APIs by default include this tool as part of Swashbuckle.
If you debug the API, and navigate to the /swagger endpoint, it should display you the Swagger documentation of your API. Which I think in turn, will satisfy what you are looking for - the routes (endpoints) of the API and what parameters they take, etc. These are your Controller methods essentially, which can be found in your Controllers folder.
An example of a basic controller method may look like:
[ApiController]
[Route("test")]
public class TestController : Controller
{
[HttpGet]
public List<MyTestModel> Get()
{
List<MyTestModel> models = new List<MyTestModel>
{
new MyTestModel
{
SomeString = "Hello world!",
SomeInteger = 101,
SomeBool = true
}
};
return models;
}
}
Notice how the class is decorated with the Route attribute. This defines the route for all the controller methods found in the controller. In this particular example, the route (or URI really) would be https://<root>/test/. Performing a GET against this resource would yield the following JSON:
[
{
"someString": "Hello world!",
"someInteger": 101,
"someBool": true
}
]
Alternatively, you can have another method with the decorator of [HttpPost], and performing a POST against the same URI would execute said method.
For more information, see the Microsoft documentation on routing in ASP.Net Core - it's very detailed!
Related
I am a newbie at ASP.NET with my second attempt at coding in .NET, so please be patient with me and thank you for your assistance with the first project. The following project is an attempt to create a .NET API that will only GET JSON for the React app to display datagrids and paginated data.
As the title states, I am not seeing the Access-Control-Allow-Origin in the header section within the developer tools - see attached screen shot.
As you can see by the prior screen shot, it is working as expected, except that the headers do not show anything regarding Access-Control-Allow-Origin, which is causing my react app to throw an error of "SyntaxError: Unexpected end of input" - when I place the JSON within a file and have the react app retrieve the local .json file, everything works as expected, which confirms, that not receiving the "Access-Control-Allow-Origin" in the headers is the issue. The following code is my Startup.cs file which uses MySql to retrieve the data.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace v2
{
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.AddCors(options =>
options.AddPolicy("AllowSpecific", p => p.WithOrigins("http://localhost:3006")
.WithMethods("GET")
.WithHeaders("name")));
services.AddMvc(option => option.EnableEndpointRouting = false);
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
//MySql
services.AddTransient<MySqlDatabase>(_ => new MySqlDatabase("server=xyz.com; database=someDB; uid=someUser; pwd=somePwd; port=somePort;"));
}
// 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.EnvironmentName == "dev")
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseCors(builder =>
builder.WithOrigins("http://localhost:3006")
.AllowAnyHeader()
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "OrderItem",
template: "OrderItem/{div?}/{yr?}",
defaults: new { controller = "OrderItem", action = "Index" });
});
}
}
}
To achieve the JSON to appear without any html tags, this was accomplished by having only #ResponseBody in the _Layout and #HTML.raw(JSON.parse(Model)) in the OrderItem/Index.cshtml page.
In PHP, I would use something like the following to show Access-Control-Allow-Origin in the headers:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Content-Type: application/json; charset=UTF-8");
Do I have to do something similar in the _Layout and cshtml pages?
One other point, I will not be able to try out your suggestions until I return to work on Monday, but as usual, thanks in advance and I appreciate your assistance
Turns out that I needed to add an attribute class, plus have it available to the entire controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Http.Filters;
namespace v2
{
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:3006");
base.OnActionExecuted(actionExecutedContext);
}
}
}
Followed by adding this to the OrderItemController:
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using dto = v2.Models;
using mFilter = v2.AllowCrossSiteJsonAttribute;
namespace v2.Controllers
{
[AllowCrossSiteJson]
While I did see a similar post with this answer, I was unable to get it to work until I added:
using mFilter = v2.AllowCrossSiteJsonAttribute;
Which made it available for the entire controller, by adding:
[AllowCrossSiteJson]
underneath the namespace and above the controller class
Sorry no screen shot, I sent an email with the screen shot to myself from work, but the system must have blocked it form arriving.
As usual, thanks in advance and thanks to #KirkLarkin for pointing out that just looking at the headers from localhost:5001 does not prove that it was not responding with the headers that I wanted to see, which lead me to view the headers from the React app's localhost:3006 perspective.
After having a hard time getting my area to show with endpoint routing i managed to fix it in this self answered thread (albeit not in a very satisfactory way) : Issue after migrating from 2.2 to 3.0, default works but can't access area, is there anyway to debug the endpoint resolution?
However Identity UI doesn't show at all for me, i get redirected on challenge to the proper url but the page is blank. I have the identity UI nugget package added and, changing from mvc routing to endpoint routing, i didn't change anything that should break it.
I also don't seem to do much different than what the default project does and identity works there even if i add a route as i did in my hack.
As often the issue hides around the line and not on it i'm posting my whole startup file.
Regular (default) controllers work.
Admin area works (one of the page doesn't have authentication and i can access it)
Any other Admin area page redirect me to /Identity/Account/Login?ReturnUrl=%2Fback (expected behavior) but that page as well as any other /Identity page i tested is blank with no error while running in debug and with a debugger attached.
Any help is most appreciated, full startup bellow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using FranceMontgolfieres.Models;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
namespace FranceMontgolfieres
{
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.AddSingleton<IConfiguration>(Configuration);
services
.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddDbContext<FMContext>(options => options
.UseLazyLoadingProxies(true)
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services
.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<FMContext>();
services
.AddMemoryCache();
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = Configuration.GetConnectionString("SessionConnection");
options.SchemaName = "dbo";
options.TableName = "SessionCache";
});
services.AddHttpContextAccessor();
services
.AddSession(options => options.IdleTimeout = TimeSpan.FromMinutes(30));
services.AddControllersWithViews();
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();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/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.UseRouting();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("Back", "Back", "back/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default","{controller=Home}/{action=Index}/{id?}");
});
}
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//initializing custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] roleNames = { "Admin", "Manager", "Member" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
}
}
The Identity UI is implemented using Razor Pages. For endpoint-routing to map these, add a call to MapRazorPages in your UseEndpoints callback:
app.UseEndpoints(endpoints =>
{
// ...
endpoints.MapRazorPages();
});
What is the alternative for context.Response.SetValidUntilExpires(true) in asp.net core?
I checked the headers in an asp.net app, and could not find any changes on setting the flag as true or false.
context.Response.Cache.SetExpires(DateTime.Now.AddDays(7));
context.Response.Cache.SetValidUntilExpires(true);
context.Response.Cache.SetCacheability(HttpCacheability.Public);
Whenever you want to cache something, do not trust the client. The client can easily ignore it and ask your API again and again.
A better approach would be to use server side caching techniques.
However, it is possible to use the ResponseCacheAttribute to solve your issue. Here is an example.
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
[ResponseCache(Duration = 123, VaryByHeader = "User-Agent")]
public IEnumerable<string> Get()
{
return new string[] {"value1", "value2"};
}
}
}
In case you are serving static files. Caching is achieved by configuring it in the Startup.cs File of your project.
Here is an example:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using Microsoft.Net.Http.Headers; // required
namespace WebApplication1
{
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var durationInSeconds = (int) TimeSpan.FromDays(1).TotalSeconds;
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = context =>
{
context.Context.Response.Headers[HeaderNames.CacheControl] =
$"public,max-age={durationInSeconds}";
}
});
app.UseMvc();
}
}
}
I'm trying to use Quartz sheduker in my asp core 2.0 project.
I downloaded Quartz 3.0.4 using nuget and after that i added services.AddQuartz(new QuartezOptions {});
to ConfigureService function in Startup.cs
I also have the same problem with app.UseQuartz()
Thats, how Startup.cs is looking now:
using AspProj.Map;
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Quartz;
namespace AspProj
{
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.AddDbContext<CacheDbContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("DB")));
services.AddScoped<CacheDbContext>();
services.AddMvc();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "API", Version = "v1" });
});
services.AddQuartz(new QuartezOptions { });
}
// 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.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "DbApi V1");
});
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseQuartz();
}
}
}
I tried to connect different Quartz namespaces through using, but it has no use.
I just keep getting "IServiceCollection does not contain definition for AddQuartz" from visual studio 2017.
error screenshot
I cant find any information about someone with the same problem as me.
Did sombody knows, how can i fix this?
Try to add the Quartz namespace in your dependencies and this should work:
using Quartz;
Also ensure that the proper package has been installed:
dotnet add package Quartz.Extensions.Hosting
need to install package Quartz.NET Microsoft.Extensions.DependencyInjection
I am building an ASP.NET Core API (1.1) in Visual Studio Code using Windows Impersonation for authentication. (The API allows researchers to create Samples.) I am using this impersonation middleware to handle authentication, which passes on the user identity nicely when connecting to the underlying SQL Server database.
However, for some write actions, I would like to add the name of the user who created the object as a value to the database (i.e. the name of the researcher creating the sample). I can't seem to make it work. I based my solution on the responses to these questions: How to get the current logged in user Id ASP.NET Core and ASP.NET Core Identity does not inject UserManager<ApplicationUser> and this tutorial, even though they seem to be aimed at storing the user identities in separate tables in the SQL server database, which is not my intent. I only need the username of the user sending the request.
I get the following error message on the line var user = await GetCurrentUserAsync(); in my controller.
The 'await' operator can only be used within an async method.
Consider marking this method with the 'async' modifier
and changing its return type to 'Task<IActionResult>'
My question is twofold:
How can I fix this error?
Is there an easier/better way to get the User Identity in my situation.
My Controller file
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;
using MyAPI.Model;
using MyAPI.Services;
namespace MyAPI.Controllers
{
[Route("api/[controller]")]
public class SamplesController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
[HttpPost]
public IActionResult Post([FromBody] Sample sample)
{
var user = await GetCurrentUserAsync();
var userId = user?.Id;
// I abstracted the underlying logic of creating a sample in the database
//because it is quite complex and doesn't seem relevant to this problem
CreateSample(sample, userId);
}
}
}
Startup.cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using MyAPI.Model;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Impersonate.AspNetCore.Windows;
namespace MyAPI
{
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)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add framework services.
services.AddMvc();
}
// 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(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseWindowsImpersonation(options => { options.Enabled = true; });
app.UseMvc();
}
}
}
MyAPI.Model.ApplicationDbContext file
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace TrinityAPI.Model
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
{
Database.EnsureCreated();
}
}
}
MyAPI.Model.ApplicationUser file
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace TrinityAPI.Model
{
public class ApplicationUser : IdentityUser
{
}
}
With Windows authentication enabled and inside the code for a controller action, you can find out information about the current user by going to the User object on the HttpContext property. For example, the following action should show domain\username for the current user.
public IActionResult Index()
{
return Content(HttpContext.User.Identity.Name);
}
When using Windows authentication, your are correct in thinking that you don't want to use ASP.NET Identity. You can remove the ApplicationDbContext class as well as the AddIdentity call in your Startup class.