I have an MVC project that I am trying to deploy to a hosting service that uses an IIS server. I have already FTP'd the files to the root directory, checked that my permissions are correct, and made sure the server is working. My problem is that it appears that the server is looking for a default.html page in my root directory, which does not exist, nor do I want it to exist. When i currently navigate to my website it says "You do not have permission to view this directory or page." The project runs perfectly in my local host. How do I get it to run on the server without creating a new index/default page?
Here is my RouteConfig file
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "EmailPDF",
url: "emailpdf",
defaults: new { controller = "Emails", action = "SendPDFEmail", }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Related
I have an MVC core application that has some folders that allow browsing.
I have used the UseDirectoryBrowser in order to map shared folders and they do appear to be browsable, the problem is that not all files are shown (specifically the files with MSI extensions).
The files do appear when they first uploaded but then disappear after a refresh of page.
We migrated to azure recently and same code was previously used on different hosting provider where we did not ever had same problems.
Is there something i could look into ?
I cannot repro your issue, but here is what I did in my side which works well. You can compare with your code:
In Startup.cs -> ConfigureServices method, add this line of code services.AddDirectoryBrowser():
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDirectoryBrowser();
}
In Startup.cs -> Configure method, add app.UseDirectoryBrowser(xxx):
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.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
RequestPath = "/MyImages"
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
RequestPath = "/MyImages"
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
After publish to azure, I can see all the files in images directory, even refresh many times:
It seems that the problem will only go away if PhysicalFileProvider receives an ExclusionFilters parameter equal to None. This makes the problem go away. Just in case some one else encounters it.
I have an Angular Web app with an API that is functioning perfectly (well, as perfectly as any app under development functions) on my local server, but when I migrate it and its associated databases to an App Service on Azure every /api call fails with 500.
So thinking that the problem was with the databases I altered the connection strings on my local development server to point to the Azure databases. I found one small problem this way, I has mispelled the username in the connection string. SO I fixed that and it runs perfectly on my local server while accessing the Azure databases, but as soon as I run it on the Azure App Service using the same connection strings every call to /api fails with Internal Server Error 500.
All regular pages are served perfectly and Angular routing works just fine. Only accessing content from the DB fails. I have been at this for a few days and have no idea what to do next. Any advice welcomed.
I am using OpenIddict for authentication so I tagged that, but I can't see anyway that is relevant. Oddly though, and there is a clue here somewhere, the authentication call to "/connect/token" works and returns a valid token, but "/api/..." URLs do not.
I am using Asp Net Core 2.1 if that is relevant.
More Information
I tried the detailed logs as suggested, but they were hardly detailed. But I did note one interesting item. In the error there was the following information:
Requested URL: https://mysite.azuurewebsites.net/api/accounts/getusers
Physical Path: D:\home\site\wwwroot\api\accounts\getusers
Now this app is using MVC so there is no such Physical Path. The Controller is decorated with:
[Route("api/accounts")]
and the Action is decorated as:
[Authorize(Roles = "Somerole")]
[HttpGet("GetUsers"), Produces("application/json")]
It seems to me the route mapping is failing. But this works beautifully on my local development computer. What could be different on the Azure App Service? Is there some special setting I need to set in the portal to allow MVC? I can't imagine why the portal should care about such matters.
Even More Information
Using Postman, if I access /api/someValidUrl with a valid Bearer token I get a 500 error. If I remove the Authorization header then I get a 401 returned.
I started off by saying I didn't think it had anything to do with OpenIddict, but maybe I was wrong. My Authorization Controller simply creates the token. All the checking for validity is done by OpenIddict.
A Huge Clue
I added an ExceptionHandler and then used Postman to make an API request and that yielded the following exception:
<h1>Error: IDX20803: Unable to obtain configuration from: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'.</h1>
at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
I found an explanation here but I don't fully understand this but it looks as though MS middleware on Azure is incorrectly trying to interpret it as an Azure AD request. The only thing I know for sure is I do not have a file called IdentityModelEventSource.cs in my project.
For reference https://mywebsite.azurewebsites.net/.well-known/openid-configuration returns:
{
"issuer": "https://mywebsite.azurewebsites.net/",
"token_endpoint": "https://mywebsite.azurewebsites.net/connect/token",
"jwks_uri": "https://mywebsite.azurewebsites.net/.well-known/jwks",
"grant_types_supported": [
"password"
],
"scopes_supported": [
"openid",
"email",
"profile",
"roles"
],
"claims_supported": [
"aud",
"exp",
"iat",
"iss",
"jti",
"sub"
],
"subject_types_supported": [
"public"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"claims_parameter_supported": false,
"request_parameter_supported": false,
"request_uri_parameter_supported": false
}
Perhaps with this information someone can point me in the right direction.
New Startup.cs
I took Pinpoint's advice and changed from JWT. The new Startup follows:
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SIAngular.DBContexts;
using SIAngular.Models;
using SIAngular.Services;
using OpenIddict.Abstractions;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using System;
using Microsoft.Extensions.Logging;
namespace SIAngular
{
public class Startup
{
private readonly IHostingEnvironment env;
public Startup(IHostingEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
this.env = env;
SIDBConnectionString = Configuration.GetConnectionString("SIDB");
}
public static string SIDBConnectionString;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
SymmetricSecurityKey _ssk = new SymmetricSecurityKey(Convert.FromBase64String(Configuration["Jwt:Key"]));
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("SqlConnection"));
options.UseOpenIddict();
});
services.AddCors();
// Register the Identity services.
services.AddIdentityCore<ApplicationUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
config.Password.RequireDigit = true;
config.Password.RequiredLength = 8;
config.Password.RequireLowercase = true; config.Password.RequireNonAlphanumeric = true;
config.User.RequireUniqueEmail = true;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddRoleValidator<RoleValidator<IdentityRole>>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>();
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict server services.
.AddServer(options =>
{
// Register the ASP.NET Core MVC services used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.UseMvc();
// Enable the token endpoint.
options.EnableTokenEndpoint("/connect/token");
options.AcceptAnonymousClients();
options.DisableScopeValidation();
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowPasswordFlow();
// Mark the "email", "profile" and "roles" scopes as supported scopes.
options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles);
// During development, you can disable the HTTPS requirement.
if (env.IsDevelopment())
options.DisableHttpsRequirement();
options.AddSigningKey(_ssk);
})
.AddValidation();
services.AddSingleton<IConfiguration>(Configuration);
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
services.AddMvc();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseWebpackDevMiddleware(new Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions { HotModuleReplacement = true });
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
});
}
}
}
Now the problem is an exception:
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
I do customer authorization Android using ASP.NET Web API Web server. I enter my login name, press enter, get an answer the user is found. But it is only through the android emulator. When I try on a real device, the error 403 port on the router and open on your computer, too. Telephone and computer on the same network. Page which is on that server opens in your phone browser if u drive a computer and the port (192.168.0.101:7899). Here is a request to the server:
#GET("/login")
void findLogin(#Query("login") String login, Callback<Users> callback);
That connection code:
public class RestClient {
private UploadService uploadService;
private String URL ="http://192.168.0.101:7899/api/";
public RestClient(){
Gson localGson = new GsonBuilder().create();
this.uploadService = ((UploadService)new RestAdapter.Builder()
.setEndpoint(URL)
.setConverter(new GsonConverter(localGson))
.setRequestInterceptor(new RequestInterceptor() {
public void intercept(RequestFacade requestFacade) {
if (URL.contains("192.168.0.101")) {
requestFacade.addHeader("Host", localhost");
}
}
})
.build().create(UploadService.class));
}
How to solve the problem with the error 403?
p.s sorry for my bad english
Assuming that you are facing issue of CORS.
So, first we need to import those packages using NuGet Package Manager. Open Package Manager Console by clicking on Tools > NuGET Package Manager > Package Manager console, there you simply enter the following command:
Install-Package Microsoft.AspNet.WebApi.Cors –IncludePrerelease
Now, In Solution Explorer, go to App_Start > WebApiConfig.cs
Add the below lines in Register(…) method:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
And
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
So, your WebApiConfig.cs would look like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Cors;
namespace Student_Management_System
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
}
}
You might want to refer my blog: https://programmingwithease.wordpress.com/2014/06/18/learning-asp-net-web-api-2-using-c/
I'm developing a website using ASP.NET MVC5 and WEB API 2.2. In my web api controller, I have an if condition to check if user is logged in:
if (!User.Identity.IsAuthenticated)
{
return BadRequest("NotLoggedIn");
}
This works fine for if there's no "www" in front of my domain name. But it will return BadRequest("NotLoggedIn") if "www" Is added to my domain name. For example, my domain is example.com. If user type www.example.com, the webpage will make an AJAX request to example.com/api/controller, and User.Identity.IsAuthenticated will return false. It will return ture if user just enter example.com.
I have enabled CORS in the global level:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("http://www.example.com", "*", "*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Anyone knows how to solve this problem?
I have solved this problem.
I need to decorate the api controller with:
[EnableCorsAttribute("http://www.example.com" , "*", "*", SupportsCredentials = true)]
I can't make attribute routing to work in Web API 2. I did not start with Web API template project, I think it was Empty project. Convention based routing works, but when I add Route attribute to controller actions, I get 404 error. I find a lot of posts regarding this, but none of them helped me. It could be related to the project template, something I have to do manually since I started with Empty project. I do call MapHttpAttributeRoutes method in WebApiConfig and the class iheriting ApiController is public. What else do I have to do in WebApiConfig class and Application_Start method?
Thanks in advance.
Here is my WebApiConfig file:
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{oId}/{oTypeCode}/{oTypeName}/{email}",
defaults: new { controller = "Xrm", email = RouteParameter.Optional, oId = RouteParameter.Optional, oTypeCode = RouteParameter.Optional, oTypeName = RouteParameter.Optional}
);
}
And Application_Start method:
protected void Application_Start()
{
System.Web.Mvc.AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
log4net.Config.XmlConfigurator.Configure();
}
Of course, my fault. I was trying to pass an email address as a path parameter. Sending it as query string parameter works. I am not sure how would it look like to send as part of the path, but it works for me this way.