I'm have a aurelia client and a webserver. When i use localhost and i'm running on the same machine it works fine.
But when i want to access the server from another machine the page loads but the api calls give the following error:
No Access-Control-Allow-Origin header is present on the requested resource.
I'm using owin and to my undestanding i need to enable CORS for owin.
I did the follwing in my startup class:-
UPDATE
I have updated my class with input from Nenad but is still get the same error.
Below i have added the call from the client.
public void Configuration(IAppBuilder app)
{
this.container = new Container();
// Create the container as usual.
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// Register your types, for instance using the scoped lifestyle:
container.Register<IWebDeps, WebDeps>(Lifestyle.Singleton);
// This is an extension method from the integration package.
container.RegisterWebApiControllers(GlobalConfiguration.Configuration, Assembly.GetExecutingAssembly());
container.Verify();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure Web API for self-host.
var config = new HttpConfiguration()
{
DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container)
};
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//// Custom Middleare
app.Use(typeof(CustomMiddleware));
app.UseWebApi(config);
//New code:
app.Run(context =>
{
context.Response.ContentType = "text/plain";
return context.Response.WriteAsync("Hello, world.");
});
}
My main program is calling the startUp class:-
using (Microsoft.Owin.Hosting.WebApp.Start<Startup>("http://localhost:8080"))
{
Console.WriteLine("Press [enter] to quit...");
Console.ReadLine();
}
Client code, 192.168.178.23 is the ip from the server.
let baseUrl2 = "http://192.168.178.23:8080/api/status/getStatus";
getStatus() {
return this.client.get(baseUrl2)
.then(response => {
return this.parseJSONToObject(response.content);
});
}
The error in Chrome:
XMLHttpRequest cannot load
http://192.168.178.23:8080/api/status/getStatus. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 400.
Cors should be enabled now right? But i still get the error when doing a api call. Am i missing any steps? Our is this approah wrong?
Any suggestions are welcome!
You have to configure WebAPI to work with CORS.
Install Nuget package:
Install-Package Microsoft.AspNet.WebApi.Cors
Enable CORS on HttpConfiguration object:
config.EnableCors();
Add [EnableCors] attribute on your controller:
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
namespace WebService.Controllers
{
[EnableCors(origins: "www.example.com", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
}
or register it globally via HttpConfig:
var cors = new EnableCorsAttribute("www.example.com", "*", "*");
config.EnableCors(cors);
More details at: Enabling Cross-Origin Requests in ASP.NET Web API 2
Add this line and check,
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
and remove,
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
this worked for me.
Turns out that when starting OWIN the adres should be http://*:8080. Instead of local host.
Related
My project is Owin self-hosted, it provides Web API endpoints and web socket endpoints.
Here is the relevant config code in the project's startup class
Owin WebSocket is used here
using Owin;
using Owin.WebSocket.Extensions;
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "api",
routeTemplate: "api/{version}/{controller}"
);
config.EnsureInitialized();
app.MapWebSocketRoute<WebSocket>("/api/v1/socket/test");
app.UseWebApi(config);
}
Works smoothly, when the app is launched I can consume the web api via "http://{host}/api/v1/test" and use the websockets by: "ws://{host}/api/v1/socket/test"
Then I decided to add some integration tests. I use Owin Test Server here. In TestServer.Create the config is identical:
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "api",
routeTemplate: "api/{version}/{controller}"
);
config.EnsureInitialized();
app.MapWebSocketRoute<WebSocket>("/api/v1/socket/test");
app.UseWebApi(config);
Test method for api
var url = new UriBuilder()
{
Scheme = "http",
Path = "/api/v1/test"
}.Uri;;
var result = client.GetAsync(url).Result;
Works nicely. But does not work for web socket:
var wsUri = new UriBuilder()
{
Scheme = "ws",
Path = "/api/v1/socket/test"
}.Uri;
//create websocket client, connect it and to server
var wsc = new ClientWebSocket();
Task.Run(async () =>
{
await wsc.ConnectAsync(wsUri, CancellationToken.None);
var a = wsc.State; // Here error thrown: No connection could be made because the target machine actively refused it 127.0.0.1:80
}).GetAwaiter().GetResult();
Why No connection could be made here? It seems like the testing server can only support regular http request not websocket. But this is not the case in the main app where the identical setting and framework is used. What am I missing here? I have been fiddling with this for hours to no avail...
Found the answer. The TestServer.Create<Startup>() only starts just the in-memory instance where the url is not available. The web socket client however relies on the url to work. So the solution is to user the overload WebApp.Start<Startup>(Settings.WebApiUrl) (it starts a web app on the url you provide)
WebApp.Start(new StartOptions("http://localhost:8989"), startup =>
{
startup.MapWebSocketRoute<TestConnection>();
startup.MapWebSocketRoute<TestConnection>("/ws", sResolver);
startup.MapWebSocketPattern<TestConnection>("/captures/(?<capture1>.+)/(?<capture2>.+)", sResolver);
});
Ref
I am trying to send a request from a Blazor(client-side) client to a server and i keep getting this error:
Access to fetch at '[route]' (redirected from '[other route]') from
origin '[origin route]' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
On the server i have already added the CORS extension in the pipeline to no avail:
Server Startup
public void ConfigureServices(IServiceCollection services) {
services.AddCors();
services.AddResponseCompression(options => {
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
MediaTypeNames.Application.Octet,
WasmMediaTypeNames.Application.Wasm,
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseResponseCompression();
app.UseMvc();
app.UseBlazor<Client.Startup>();
}
Blazor Client request
public async Task<Catalog> GetCatalogAsync() {
try {
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG), //BASE_PATH= 172.XX.XX.XX:8600
Method = HttpMethod.Get
};
var resp = await this.client.SendAsync(message); // client is HttpClient
var resultString = await resp.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Catalog>(resultString);
return data;
} catch (Exception ex) {
throw;
}
}
Controller
[HttpGet]
[Route(Routes.GET_CATALOG)]
public async Task<Catalog> GetCatalogAsync() {
try {
var registry = await this.adminService.GetCatalogAsync();
return registry;
} catch (Exception ex) {
throw;
}
}
POCO
[Serializeable]
public struct Catalog{
}
What else can i do to be able to reach my server? Is it due to Blazor ?
As you can see i have already added the UseCors(...).
P.S
I have published my Blazor Server project together with the Client.They are in the same directory.This folder i placed it on a computer,and i am trying from my computer to open blazor : 172.168.18.22:8600/
Update
I have also tried adding headers to my HttpRequestMessage to no avail:
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG),
Method = HttpMethod.Get,
};
message.Headers.Add("Access-Control-Allow-Origin","*");
message.Headers.Add("Access-Control-Allow-Credentials", "true");
message.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Origin,Content-Type");
#Bercovici Adrian, why do you add CORS support to your App ? Do you make cross origin requests ? If you don't, don't try to solve the issue by adding unnecessary configuration that may lead to more subtle bugs.
As usual, without seeing a repo of this app, can't help you any further.
Update:
What is this UseBlazor ?
You should upgrade your app to the latest version...
New Update:
Sorry, but I'm using the current preview version of Blazor
Startup class
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.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
**// Instead of UseBlazor**
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseStaticFiles();
app.UseRouting();
**// This configure your end points**
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
Note that I've removed the configuration of CORS as your client and server share the same domain. Please use the docs how to configure CORS appropriately.
Try this and see if it is working for you (I guess your issue is related to the configuration of the endpoints. Somehow, it seems to me that because you did not configure the endpoints, your request is redirected, and thus you get the message displayed by you above.)
Next to do is to check if your http request was appropriately cooked. But first checks the end points.
Somehow the problem was due to a very old client version that was cached on the browser.Never again will i forget to clear the browser cache after this problem.
Thank you all for your help and support !
Check that you do not send HTTP requests when running from HTTPS. For example if you send requests to http://172.168.18.22:8600 when your application was opened in https://172.168.18.22:8600 you may have an issue.
you need to specify your policy name in the middleware.
builder.Services.AddCors(policy =>{
policy.AddPolicy("Policy_Name", builder =>
builder.WithOrigins("https://*:5001/")
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyOrigin()
);});
// Configure the HTTP request pipeline.
app.UseCors("Policy_Name");
I tried everything but can't enable CORS for my WebApi project. I guess I 'm missing something or not doing it right.
My StartUp.config is:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
// Web API routes
//app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
config.MapHttpAttributeRoutes();
config.EnableCors(new System.Web.Http.Cors.EnableCorsAttribute("http://www.test.ca", "*", "GET,POST")); //enable only for this domain
ConfigureOAuth(app);
app.UseWebApi(config);
ConfigureAutofac(app, config);
}
My api controller:
[HttpPost]
[Authorize]
[Route("api/Accounts/GetTestTest")]
[System.Web.Http.Cors.EnableCors("http://www.test.ca", "*", "*")]
public HttpResponseMessage GetTestTest()
{
return this.Request.CreateResponse(System.Net.HttpStatusCode.OK);
}
Here I should be restricted because my request are made from MVC application which runs on localhost. Also I'm using tokens to authorize users.
Any ideas what I am missing or doing wrong?
EDIT Request is comming from MVC controller action like this:
static string CallApi(string url, string token, LogInRequest request)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient())
{
if (!string.IsNullOrWhiteSpace(token))
{
var t = Newtonsoft.Json.JsonConvert.DeserializeObject<CashManager.Models.Global.Token>(token);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
}
var response = client.PostAsJsonAsync<string>(url,string.Empty).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
CORS does not apply to requests made from a back-end. It only applies to requests coming from browsers via AJAX.
You will need to do IP address-based filtering or something else to block requests from certain places. The authentication you have might be good enough though.
I create an asp.net 5 project with visualstudio 2015 ctp.
As you know it prepares identity system. there is a method called Register at accountcontroller and when I test it it works properly but when I add the following code to it :
before adding the new code
await SignInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
after adding the new codes :
await SignInManager.SignInAsync(user, isPersistent: false);
// new lines for adding the new Role to the database
ApplicationDbContext adbc = new ApplicationDbContext();
var roleStore = new RoleStore<IdentityRole>(adbc);
var roleManager = new RoleManager<IdentityRole>(roleStore);
await roleManager.CreateAsync(new IdentityRole { Name = "Administrator" });
// end of the new lines
return RedirectToAction("Index", "Home");
but after adding this new lines the following error returns :
InvalidOperationException: A relational store has been configured without specifying either the DbConnection or connection string to use.
It seems we must initialize dbcontext for role manager at startup. the start up code already is :
public void ConfigureServices(IServiceCollection services)
{
// Add EF services to the services container.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<ApplicationDbContext>();
// Add Identity services to the services container.
services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
.AddEntityFrameworkStores<ApplicationDbContext>();
// Add MVC services to the services container.
services.AddMvc();
// Uncomment the following line to add Web API servcies which makes it easier to port Web API 2 controllers.
// You need to add Microsoft.AspNet.Mvc.WebApiCompatShim package to project.json
// services.AddWebApiConventions();
}
// Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
// Configure the HTTP request pipeline.
// Add the console logger.
loggerfactory.AddConsole();
// Add the following to the request pipeline only in development environment.
if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
{
app.UseBrowserLink();
app.UseErrorPage(ErrorPageOptions.ShowAll);
app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
}
else
{
// Add Error handling middleware which catches all application specific errors and
// send the request to the following path or controller action.
app.UseErrorHandler("/Home/Error");
}
// Add static files to the request pipeline.
app.UseStaticFiles();
// Add cookie-based authentication to the request pipeline.
app.UseIdentity();
// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
// Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
}
Any idea?
I am using token authentication for small project based on this article: http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/
Everything seems to work fine except one thing: OWIN based token authentication doesn't allow OPTIONS request on /token endpoint. Web API returns 400 Bad Request and whole browser app stops sending POST request to obtain token.
I have all CORS enabled in application as in sample project. Below some code that might be relevant:
public class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
HttpConfiguration config = new HttpConfiguration();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseWebApi(config);
Database.SetInitializer(new ApplicationContext.Initializer());
}
public void ConfigureOAuth(IAppBuilder app)
{
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
Below is my login function from javascript (I am using angularjs for that purpose)
var _login = function (loginData) {
var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
data = data + "&client_id=" + ngAuthSettings.clientId;
var deferred = $q.defer();
$http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, refreshToken: response.refresh_token, useRefreshTokens: true });
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
_authentication.useRefreshTokens = loginData.useRefreshTokens;
deferred.resolve(response);
}).error(function (err, status) {
_logOut();
deferred.reject(err);
});
return deferred.promise;
};
var _logOut = function () {
localStorageService.remove('authorizationData');
_authentication.isAuth = false;
_authentication.userName = "";
_authentication.useRefreshTokens = false;
};
I've lost some time on this problem today. Finally i think i've found a solution.
Override method inside your OAuthAuthorizationServerProvider:
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization" });
context.RequestCompleted();
return Task.FromResult(0);
}
return base.MatchEndpoint(context);
}
This appears to do three necessary things:
Force auth server to respond to OPTIONS request with 200 (OK) HTTP status,
Allow request to come from anywhere by setting Access-Control-Allow-Origin
Allows Authorization header to be set on subsequent requests by setting Access-Control-Allow-Headers
After those steps angular finally behaves correctly when requesting token endpoint with OPTIONS method. OK status is returned and it repeats request with POST method to get full token data.
Override this method inside your OAuthAuthorizationServerProvider:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
Are you running it locally or are you publishing it to Azure like in the blog article's sample code?
If you're running it on Azure, you can easily fix CORS problems by enabling CORS in the Azure portal:
Click on your App Service in the Azure Portal to enter the management screen.
In the list of management options, scroll down to the 'API' section, where you will find the 'CORS' option. (Alternatively type 'CORS' in the search box).
Enter the allowed origin, or enter '*' to enable all, and click save.
This fixed the OPTIONS preflight check problem for me, which a few other people seem to have had from the code in that particular blog article.
Solved it. The problem was not sending with OPTIONS request header Access-Control-Request-Method
This should do the trick:
app.UseCors(CorsOptions.AllowAll);