I search github source and http://docs.asp.net/en/latest, but can't find any documentation for RouteExistingFiles. I've tried adding it onto routes.RouteExistingFiles, but this won't compile. Has this option be removed or rethought? Can it be accessed from the StartUp?
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
StaticFileOptions option = new StaticFileOptions();
FileExtensionContentTypeProvider contentTypeProvider = (FileExtensionContentTypeProvider) option.ContentTypeProvider;
contentTypeProvider.Mappings.Add(".yqs", "text/plain");
app
.UseStaticFiles(option)
.UseDefaultFiles()
.UseFileServer()
.UseMvc(routes =>
{
routes.MapRoute(
"YQ Controller",
"{*src}",
new { controller = "YQFile", action = "OnDemand" },
new { src = #"(.*?)\.(yqs)" }
);
});
}
}
Though I can't find any documentation, it seems this option isn't in asp.net 5. Now, it seems that routing and other configuration in StartUp.Configure() will take precedence over proceeding configuration. So, bringing the app.UseMvc() configuration forward in the chain will allow the route to take priority over app.UseStaticFiles().
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
app
.UseMvc(routes =>
{
routes.MapRoute(
"YQ Controller",
"{*src}",
new { controller = "YQFile", action = "OnDemand" },
new { src = #"(.*?)\.(yqs)" }
);
})
.UseStaticFiles()
.UseDefaultFiles()
.UseFileServer();
}
}
Related
I'm trying to implement Azure App Configuration to my Application that uses the ASP.NET Boilerplate Framework. I'm following this tutorial but when I try to access my settings everything comes null. When the Startup.cs get executed I can see the values in the constructor but when I try to get them else where I get the null.
Program.cs:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((config) =>
{
// Retrieve the connection string
IConfiguration settings = config.Build();
string connectionString = settings.GetConnectionString("AppConfig");
// Load configuration from Azure App Configuration
config.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
// Load all keys that start with `TestApp:` and have no label
.Select("TestApp:*", LabelFilter.Null)
// Configure to reload configuration if the registered sentinel key is modified
.ConfigureRefresh(refreshOptions => refreshOptions.Register("TestApp:Settings:Sentinel", refreshAll: true));
}).Build();
})
.UseStartup<Startup>()
.Build();
}
}
Startup.cs:
public class Startup
{
private const string _defaultCorsPolicyName = "localhost";
private const string _apiVersion = "v1";
public IConfigurationRoot _appConfiguration;
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
_appConfiguration = env.GetAppConfiguration();
Configuration = configuration; //Azure App Configuration
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//MVC
services.AddControllersWithViews(
options =>
{
options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
}
).AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new AbpMvcContractResolver(IocManager.Instance)
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
services.AddSignalR();
// Configure CORS for angular2 UI
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
)
);
options.DocInclusionPredicate((docName, description) => true);
// Define the BearerAuth scheme that's in use
options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
});
services.AddAzureAppConfiguration();
// Bind configuration "TestApp:Settings" section to the Settings object
services.AddOptions();
services.Configure<Settings>(Configuration.GetSection("TestApp:Settings"));
// Configure Abp and Dependency Injection
return services.AddAbp<RptWebHostModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework.
app.UseCors(_defaultCorsPolicyName); // Enable CORS!
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAbpRequestLocalization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>("/signalr");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
});
options.IndexStream = () => Assembly.GetExecutingAssembly()
options.DisplayRequestDuration(); // Controls the display of the request duration (in milliseconds) for "Try it out" requests.
});
// Use Azure App Configuration middleware for dynamic configuration refresh.
app.UseAzureAppConfiguration();
}
}
Custom Controller where I get the null values:
[Route("api/[controller]/[action]")]
public class AzureAppConfigTest : AbpControllerBase
{
public Settings _settings { get; }
public AzureAppConfigTest(IOptionsSnapshot<Settings> options
)
{
_settings = options.Value;
}
[HttpPost]
public string Test()
{
return _settings.Message; // The Problem is here
}
}
I need to get the values else where in the Application, I tried changing IOptionsSnapshot for IOptions but I can't make it work, Iv'e been stuck with this about two week but since I'm new in the Microsoft world I can't see clearly where the problem is, Thanks in Advance
Update:
I am able to use the configuration at the Presentation Layer, but If I try to use it on the Application layer I don't get the values.
I have read multiple solutions but somehow none worked for me. I am still getting CORS error. I am using a react front end and tried all solutions related to the frontend. I think my problem has to do with the backend .NET
this is how my controller CORS settings look like. I tried the wild card but it does not work either, I am now trying with the frontend address and port and still having CORS issues. any suggestion ?
[EnableCors(origins: ALLOWED_CORS_ORIGINS, headers: "*", methods: "*", SupportsCredentials = true)]
public class TroubleshootingDataController : ApiController
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string ALLOWED_CORS_ORIGINS = "http://localhost:3000/";
startUp.cs
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
if (!Debugger.IsAttached)
{
//ConfigureAuth(app);
GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnection", new SqlServerStorageOptions { SchemaName = "HangfireConnLiving" });
var filter = new BasicAuthAuthorizationFilter(
new BasicAuthAuthorizationFilterOptions
{
RequireSsl = true,
LoginCaseSensitive = true,
Users = new[]
{
new BasicAuthAuthorizationUser
{
Login = "",
PasswordClear = ""
}
}
});
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[]
{
filter
}
});
app.UseHangfireServer();
}
}
}
}
On the backend on the Startup.cs just add this
On configure.
public void Configure(IApplicationBuilder appBuilder, IWebHostEnvironment webHostEnvironment)
{
appBuilder.UseCors(); // this line must before the appBuilder.UseEndPoints();
}
On ConfigureServices, we configure the cors
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
});
});
}
Edit:
It seams you are using a diferent framework or something like that, that I'm used to work with.
try to add this
app.UseCors(CorsOptions.AllowAll);
before the if (!Debugger.IsAttached)
I'm building a GraphQL API in Asp Net Core 3.1. I'm trying to add a subscription that send a new entity to subscriber when it is added.
When I execute the subscription from the /ui/playground the handshake with server seems to be successed:
GraphQL http request websocket:
When I execute the mutation that adds a review it successed and create a record on the db, but the above screen reamins as it is, no updates, no data receveived from the socket.
This is the mutation:
Field<ReviewType>(
"createReview",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<ReviewInput>>
{
Name = "reviewInput"
}
),
resolve: context =>
{
var review = context.GetArgument<Review>("reviewInput");
var rev = reviewService.Add(review);
return rev;
}
);
ShopSubscrition.cs
public partial class ShopSubscription : ObjectGraphType
{
private readonly IReviewService _reviewService;
public ShopSubscription(IReviewService reviewService)
{
_reviewService = reviewService;
AddField(new EventStreamFieldType
{
Name = "reviewAdded",
Type = typeof(ReviewType),
Resolver = new FuncFieldResolver<Review>((context) => context.Source as Review),
Subscriber = new EventStreamResolver<Review>((context) => _reviewService.ReviewAdded())
});
}
}
ReviewService.cs
public class ReviewService : IReviewService
{
private readonly IReviewRepository _reviewRepository;
private readonly ISubject<Review> _sub = new ReplaySubject<Review>(1);
public ReviewService(IReviewRepository reviewRepository)
{
_reviewRepository = reviewRepository;
}
public Review Add(Review review)
{
var addedEntity = _reviewRepository.Add(review);
_sub.OnNext(review);
return review;
}
public IObservable<Review> ReviewAdded()
{
return _sub.AsObservable();
}
}
I post the Startup.cs too, maybe it can helps.
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddDbContext<ShopDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("Default")));
services.AddScoped<ISupplierRepository, SupplierRepository>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IReviewRepository, ReviewRepository>();
services.AddScoped<IReviewService, ReviewService>();
services.AddSingleton<IDataLoaderContextAccessor, DataLoaderContextAccessor>();
services.AddSingleton<DataLoaderDocumentListener>();
services.AddScoped<ShopSchema>();
services.AddGraphQL(options =>
{
options.EnableMetrics = false;
})
.AddWebSockets()
.AddSystemTextJson()
.AddGraphTypes(typeof(ShopSchema), ServiceLifetime.Scoped).AddDataLoader();
services.AddControllers()
.AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ShopDbContext dbContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("Cors");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQLWebSockets<ShopSchema>("/graphql");
app.UseGraphQL<ShopSchema>();
app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions { GraphQLEndPoint ="/graphql", SchemaPollingEnabled = false });
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
dbContext.SeedData();
}
}
Thanks in advance for help.
After a deeper investigation I found out the problem. I post the solution, maybe could help someone.
The problem was about Dependency Injection: the class containing the subscription "notification logic" (in my case ReviewService.cs) must be registered as Singleton instead of Scoped. This caused a sort of chain reaction that bring the repositories classes to be registered as Transient.
This is the working code:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("Cors",
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddDbContext<ShopDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("Default")));
services.AddTransient<ISupplierRepository, SupplierRepository>();
services.AddTransient<IProductRepository, ProductRepository>();
services.AddTransient<IReviewRepository, ReviewRepository>();
services.AddSingleton<IReviewService, ReviewService>();
services.AddSingleton<IDataLoaderContextAccessor, DataLoaderContextAccessor>();
services.AddSingleton<DataLoaderDocumentListener>();
services.AddScoped<ShopSchema>();
services.AddGraphQL(options =>
{
options.EnableMetrics = false; // info sulla richiesta (ms, campi richiesti, ecc)
})
.AddWebSockets()
.AddSystemTextJson()
.AddGraphTypes(typeof(ShopSchema), ServiceLifetime.Scoped).AddDataLoader();
services.AddControllers()
.AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
I am looking for sample code and examples regarding how to implement authorization at resolver function level using GraphQL.NET and ASP.NET CORE 2.
Basically I am trying to prevent the execution of query if the request is not authorized.
Can anyone help me to get some good tutorials or code samples as reference for the implementation.
For graphql-dotnet/authorization, the page for AspNetCore has not been released, refer Add GraphQL.Server.Authorization.AspNetCore NuGet package #171.
You could implement Authorization.AspNetCore for your own use.
After implement Authorization.AspNetCore, you could configure the Authorize like:
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
Configuration = configuration;
Environment = hostingEnvironment;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
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.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
services.AddGraphQL(options =>
{
options.EnableMetrics = true;
options.ExposeExceptions = Environment.IsDevelopment();
//options.
})
.AddGraphQLAuthorization(options =>
{
options.AddPolicy("Authorized", p => p.RequireAuthenticatedUser());
//var policy = new AuthorizationPolicyBuilder()
// .
//options.AddPolicy("Authorized", p => p.RequireClaim(ClaimTypes.Name, "Tom"));
});
//.AddUserContextBuilder(context => new GraphQLUserContext { User = context.User });
services.AddSingleton<MessageSchema>();
services.AddSingleton<MessageQuery>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// 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.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseGraphQL<MessageSchema>("/graphql");
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions()
{
Path = "/ui/playground"
});
app.UseGraphiQLServer(new GraphiQLOptions
{
GraphiQLPath = "/ui/graphiql",
GraphQLEndPoint = "/graphql"
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Schema
public class MessageQuery : ObjectGraphType<Message>
{
public MessageQuery()
{
Field(o => o.Content).Resolve(o => "This is Content").AuthorizeWith("Authorized");
Field(o => o.SentAt);
Field(o => o.Sub).Resolve(o => "This is Sub");
}
}
For complete demo, refer GraphQLNet.
To get GraphQL.Net's authorization to work in ASP.NET Core, first install this package:
GraphQL.Server.Authorization.AspNetCore
In Startup.cs add the following in ConfigureServices. Make sure to add these using statements:
using GraphQL.Validation;
using GraphQL.Server.Authorization.AspNetCore;
public void ConfigureServices(IServiceCollection services)
{
//... other code
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services
.AddTransient<IValidationRule, AuthorizationValidationRule>()
.AddAuthorization(options =>
{
options.AddPolicy("LoggedIn", p => p.RequireAuthenticatedUser());
});
//... other code
}
Now you'll be able to use AuthorizeWith() at the resolver level to protect the field. Example:
public class MyQuery : ObjectGraphType
{
public MyQuery(ProductRepository productRepository)
{
Field<ListGraphType<ProductType>>(
"products",
resolve: context => productRepository.GetAllAsync()
).AuthorizeWith("LoggedIn");
}
}
You can also protect all queries by adding this.AuthorizeWith() to the top of the Query constructor like this:
public class MyQuery : ObjectGraphType
{
public MyQuery(ProductRepository productRepository)
{
this.AuthorizeWith("LoggedIn");
Field<ListGraphType<ProductType>>(
"products",
resolve: context => productRepository.GetAllAsync()
);
}
}
With that, any unauthenticated access to your GraphQL endpoint will be rejected.
Now in terms of logging someone in, there are many ways to do that. Here's a quick Cookie based authentication example:
Configure cookie based authentication in Startup.cs' ConfigureServices:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(o =>
{
o.Cookie.Name = "graph-auth";
});
Use mutation to log someone in:
public class Session
{
public bool IsLoggedIn { get; set; }
}
public class SessionType : ObjectGraphType<Session>
{
public SessionType()
{
Field(t => t.IsLoggedIn);
}
}
public class MyMutation : ObjectGraphType
{
public MyMutation(IHttpContextAccessor contextAccessor)
{
FieldAsync<SessionType>(
"sessions",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "password" }),
resolve: async context =>
{
string password = context.GetArgument<string>("password");
// NEVER DO THIS...for illustration purpose only! Use a proper credential management system instead. :-)
if (password != "123")
return new Session { IsLoggedIn = false };
var principal = new ClaimsPrincipal(new ClaimsIdentity("Cookie"));
await contextAccessor.HttpContext.SignInAsync(principal, new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMonths(6),
IsPersistent = true
});
return new Session { IsLoggedIn = true };
});
}
}
This is my Startup.cs where I map my index page to the route '/app'.
...
using Microsoft.Owin.FileSystems;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.Diagnostics;
[assembly: OwinStartup(typeof(conApi.Startup))]
namespace conApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
////Set static files
ConfigureFiles(app);
//Enable Cors
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
public void ConfigureFiles(IAppBuilder app)
{
app.Map("/app", spa =>
{
spa.Use((context, next) =>
{
context.Request.Path = new PathString("/index.html");
return next();
});
spa.UseStaticFiles();
});
}
}
}
It works like a charm but I don't know how to configure the client caching. I would like to know how to set the Expires header if that is possible when using OWIN static files?
SOLUTION
Tratcher provided the link to the StaticFilesOptions class documentation etc which lead me to a solution. Added the StaticFilesOptions to the ConfigureFiles method like this:
public void ConfigureFiles(IAppBuilder app)
{
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = (StaticFileResponseContext) =>
{
StaticFileResponseContext.OwinContext.Response.Headers.Add("Cache-Control",new[] { "public", "max-age=1000" });
}
};
app.Map("/app", spa =>
{
spa.Use((context, next) =>
{
context.Request.Path = new PathString("/index.html");
return next();
});
spa.UseStaticFiles(staticFileOptions);
});
}
You can pass a StaticFilesOptions to UseStaticFiles. On the options use the OnPrepareResponse event to customize your responses. See http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.StaticFiles/StaticFileOptions.cs