Asp.net web API CORS series/mysterious issue - asp.net

I am facing a CORS policy problem and I do not know how to fix it. I tried many approaches but what makes me crazy is that the service works fine on some devices and I can utilize all its resources and works a little bit on others and does not work at others while the whole devices having the same configuration equally set. To be more clear I am having a Web application based entirely and only on AngularJS 2 and a Web API that exposes a few actions. I installed the IIS and hosted the Web API on the default Web Site, which means it can be called from port 80 for simplicity, and I hosted the Web application using port 4200. Now let's give more detail about my Web API application structure.
EMG.WebApi.Core -> this project is the core project in which I put the controller classes and the web configuration class
EMG.WebApi.WebHost -> this project is used merely for hosting and it contains a reference to the EMG.WebApi.Core project and is the one that contains the Global.asax and within its Application_Start I am calling the Register method of Configuration class of the WebApi.Core and give it as a parameter the GlobalConfiguration object to register my handlers, tracers etc.
using EMG.ElectrometerWS.WebApi.Core;
using System;
using System.Web.Http;
using EMG.ElectrometerWS.WebApi.Core.Configuration;
namespace EMG.ElectrometerWS.WebApi.WebHost
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
GlobalConfiguration.Configuration.EnsureInitialized();
}
}
}
using EMG.ElectrometerWS.WebApi.Core.Handlers;
using EMG.ElectrometerWS.WebApi.Core.Tracer;
using System;
using System.Configuration;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Http.Tracing;
namespace EMG.ElectrometerWS.WebApi.Core.Configuration
{
public static class WebApiConfig
{
...
public static string CorsOrigin
{
get
{
string result =
ConfigurationManager.AppSettings.Get("CorsOrigin");
if (!string.IsNullOrEmpty(result))
{
return result;
}
throw new Exception("AppSetting CorsOrigin not found in
web.config file.");
}
}
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.Routes.MapHttpRoute(
// name: "Secret Api",
// routeTemplate: "secretapi/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional },
// constraints: null,
// handler: new ApiKeyProtectionMessageHandler() {
// InnerHandler = new HttpControllerDispatcher(config)
// });
// Enable ASP.NET Web API tracing
//config.EnableSystemDiagnosticsTracing();
//config.Services.Replace(typeof(ITraceWriter), new
// EntryExitTracer());
//config.Services.Replace(typeof(ITraceWriter), new WebApiTracer());
//config.MessageHandlers.Add(new EmptyPostBodyMessageHandler());
// Message handler to check the performance in production
environment ONLY
config.MessageHandlers.Add(new TracingHandler());
//config.MessageHandlers.Add(new XHttpMethodOverrideHandler());
config.MessageHandlers.Add(new JwtHandler());
}
}
}
EMG.ElectrometerWS.WebApi.WebHost Web.Cofig
<appSettings>
....
<add key="CorsOrigin" value="http://localhost:4200"/>
</appSettings>
What makes me crazy is that everything works fine on my colleague laptop and he can use all the actions. On mine I cannot call some of PUT methods while I can for others on other colleague/testers they can only call GET methods!!! And increases my surprises is that after clearing the browser history/cookies one of those laptops that have only GET methods being called have all things works fine.
What I have tried:
First I added the below code as you can notice above to the configuration class
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
Creating the following handler and registered it as the first handler before other handlers
public class CorsPreflightHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
request,
CancellationToken cancellationToken)
{
if (request.Headers.Contains("Origin") && request.Method == HttpMethod.Options)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "*");
return response;
}
return await base.SendAsync(request, cancellationToken);
}
}
Removing the previous code and configured the CORS using the Web.config file of the WebHost project
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http:localhost:4200" />
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
Finally removing the web config tags and enabled if on each controller class
[EnableCors(origins: "http://localhost:4200", headers: "*", methods: "*")]
public class XXXController : ApiController
{
Public string Get(int id)
{
return "value";
}
}

The CORS issue seems solved in meantime. I used the first approach by setting an application setting key/value for the allowed Origin then use that key/value to retrieve and register that Origin through a CORS attribute.
<appSettings>
....
<add key="CorsOrigin" value="http://localhost:4200"/>
</appSettings>
public static string CorsOrigin
{
get
{
string result =
ConfigurationManager.AppSettings.Get("CorsOrigin");
if (!string.IsNullOrEmpty(result))
{
return result;
}
throw new Exception("AppSetting CorsOrigin not found in web.config file.");
}
}
public static void Register(HttpConfiguration config)
{
....
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
}
However, I still do not know what cause the problem from the beginning it may be an IIS issue or missing feature etc.

Related

ASP.NET ApiController inside a webform can't reach methods

I can't reach any methods from my ApiController in anyway, the routing does appear if i try to reach it by a browser but no methods are shown.
My Controller:
namespace AgroRiego.Controllers
{
public class datacontrol : ApiController
{
[HttpGet, Route("api/get")]
public string Get([FromUri]string user, string pass)
{
string check = SQL.Reader("SELECT * FROM users WHERE username='" + user + "' AND password='" + pass + "'");
if (String.IsNullOrWhiteSpace(check))
{
return "error en credenciales";
}
DataTable horarios = SQL.table_read("SELECT * FROM horario_riego");
string json = Utils.ConvertDataTabletoJSON(horarios);
return json;
}
[HttpPost, Route("api/post")]
public void Post([FromBody]string value)
{
string i = value;
}
}
}
my global asax:
namespace AgroRiego
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
and my webapiconfig:
namespace AgroRiego
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ConfiguraciĆ³n y servicios de API web
// Rutas de API web
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
i have more webforms inside the project (originally it was just html pages with serverside code, but i need to add a couple methods to retrieve and send data, help much appreciated!
EDIT1: i managed to reach HTTP 200 changing the URL but i can't reach the methods anyway (in debug mode it does not stop on the breakpoints) how can i route correctly the Api (so it is not Login.aspx) and how do i fix the methods reaching?
EDIT2: i read in documentation that i need this line in global:
RouteConfig.RegisterRoutes(RouteTable.Routes);
but im not using MVC does that matter? i tried reaching the routes with a brand new MVC Web Api and it yields "No Response"
use a routerprefix with your controller. So you access the URL as
http://localhost/routerprefix/router
HttpClient class can be use to send and receive HTTP requests and responses. Since you are trying to consume a WebApi from a aspx page, better way is to create a HttpClient instance
Below is a very simple implementation. Please check this url for further information
MSDN sample
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:49342/api/get");
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync();
}
By the look of your set up, it seems correct
you have got:
config.MapHttpAttributeRoutes(); - setup the attribute route
config.Routes.MapHttpRoute( - setup the default route
GlobalConfiguration.Configure(WebApiConfig.Register); - to register at startup
so it should work.
I think the problem you are having is the way you are calling it
WebAPI routing work a little different to MVC
for example:
In you get method, the route is set as below
[HttpGet, Route("api/get")]
so you should call it {host}/api/get using a GET http method
in the screen shot, you are calling using {host}/api/get/Get - which would not have work, because no route would match
Same for your POST method
So give it another try and you should be able to reach it
The URL to add in the rest testing tool is
http://localhost:49342/api/get
Method type is GET
If you are calling this web api from aspx page use the httpClient class.

Angularjs cant find view, tried everything I know

I have made a project using ng.Net template from visual studio, i got it up and running, i added a new ProgramController.cs, Program.cshtml template, programCtrl.js angular controller, and a program angular module.
And it just will not work.
I have a ASP.NET web api and angularjs on clientside.
Here are 2 example routes:
$routeProvider.when('/todomanager', {
templateUrl: 'App/TodoManager',
controller: 'todoManagerCtrl'
});
$routeProvider.when('/program', {
templateUrl: 'App/Program',
controller: 'programCtrl'
});
And what they do backend:
[HttpGet]
[Authorize]
public List<Program> GetPrograms()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.Programs;
//return db.Programs;
}
[HttpGet]
[Authorize]
public List<todoItem> GetUserTodoItems()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.todoItems;
}
The first one works, the second one gives tpload compile error (could not find template view)
I can get the TodoManager view if I call it in /program
But I cant reach my program.cshtml. It's in the same folder as TodoManager.cshtml
I could provide more code, But I dont know which, Because it all works.
My closest guess so far, is that I dont have access to that view, I'm being blocked by a blockviewhandler or the view doesnt exist.
I have the access.
If it was the viewblockhandler it would also block TodoManager.cshtml
And it exists xD I'm looking at it..
It's like i'm spamming 4 + 4 on a calculator and it keeps returning 5..
Please, anything, been stuck for 2 workdays.
EDIT additional code
//RouteConfig RegisterRoutes
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "App",
url: "{url}",
defaults: new { controller = "Main", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Main", action = "Index", id = UrlParameter.Optional }
);
}
//WebApiConfig Register
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
//I chaned the routeTemplate so that methods/services would be identified by their action, and not by their parameters.
//I was getting conflicts if I had more than one GET services, that had identical parameter options, but totally different return data.
//Adding the action to the routeTemplte correct this issue.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}", //routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
//blockviewhandler in Web.config
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*." verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
Edit: Project structure, i have a HomeController that returns my Index.cshtml in there, i have my ng-view which is where my views should be rendered.
Many seem to misunderstand my real question, i want to know:
How is it, that the todoManager works, and the program does not.
When using ASP.NET MVC, Web API etc. you should add "every" view to the mvc controller, you probably have something like this in one of your controllers:
public ActionResult TodoManager()
{
return PartialView();
}
assuming you are following a tutorial or modifying a sample it is probably in Controllers/AppController. If true you must add your another view as:
public ActionResult Program()
{
return PartialView();
}

Why is the ASP.NET routing engine ignoring files?

I have a class that implements both IHttpHandler and IRouteHandler:
public class CustomHandler : IHttpHandler,IRouteHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.AddHeader("Content-Type", "text/plain");
context.Response.Write("Hello World");
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return this;
}
}
In the Application_Start method I try to register my handler with route:
Route route = new Route("dav/{*Pathinfo}", new CustomHandler());
RouteTable.Routes.Add(route);
Everything is cool until I call with this kind Url:
- http://localhost:63428/dav/asdadsa
- http://localhost:63428/dav/asdadsa/asdasd
- http://localhost:63428/dav/asdadsa/a%20%20sdasd (with space in the url)
But if I try with theese:
- http://localhost:63428/dav/asdadsa.docx
- http://localhost:63428/dav/asdads/a.docx
My handler not called and the server return with 404. I thought that the wildcard will match every url which start with dav/.
Any idea how to achive that the urls with extensions also match to my route?
UPDATE:
I found this page.
It's set from config, not from code behind but don't have to set the runAllManagedModulesForAllRequests settings and unfortunately don't get the route values so clean that in my original example.
Maybe somebody will be intrested if come to this question for answers.
If you add the following configuration to your web.config file then your routing will include files as well.
<configuration>
<system.webServer>
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
<!-- more -->
</modules>
</system.webServer>
</configuration>
One different solution is to add <modules runAllManagedModulesForAllRequests="true"> but that gives an overhead in that all your registered HTTP modules run on every request, not just managed requests (e.g. .aspx). This means modules will run on every .jpg .gif .css .html .pdf etc.
You can read more here about the different routing settings.
Just remember, if you're adding your special routing to the existing routing you have to add your routing first or it won't be processed, as in this example.
Route route = new Route("dav/{*Pathinfo}", new CustomHandler());
routes.Add(route);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
One problem with this approach is that the first route definition confuses Html helpers in your system so you won't any longer get nice routes like localhost/home/index but instead localhost/dav?action=index&controller=home. The solution to this is to restrict the first route to only be valid on incoming route requests. This can be done by creating a RouteConstraint and add it to the route definition in a RouteValueDictionary.
public class OnlyIncomingRequestConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest)
return true;
return false;
}
}
You can then redefine your route definition in this way:
Route route = new Route("dav/{*Pathinfo}", new RouteValueDictionary(new { controller = new OnlyIncomingRequestConstraint() }), new CustomHandler());
routes.Add(route);
After this your default routes should be back to normal again.

Ajax Url to Routed WCF in Asp.Net 4 Web Forms

I implemented Routing in Asp.Net 4 Web App.
With aspx file it works fine, but it doesn't work with WCF.
I have WCF which is called from javascipt file by ajax request.
My WCF code is:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Signature
{
/// <summary>
/// Get signature info by email address.
/// </summary>
[OperationContract]
[WebInvoke(ResponseFormat = WebMessageFormat.Json)]
public string GetSignatureInfo(string email)
{
...
}
}
}
Web.config:
<services>
<service name="WEB.Services.Signature">
<endpoint address="" behaviorConfiguration="WEB.Services.SignatureAspNetAjaxBehavior"
binding="webHttpBinding" contract="WEB.Services.Signature" />
</service>
</services>
javascript:
var _data = {
"email": self.email.val()
};
$.ajax({
url: '../Signature'
, type: 'POST'
, dataType: 'json'
, data: JSON.stringify(_data)
, success: function (data) {
console.log('success');
}
, error: function () {
console.log('error');
}
});
Global.asax:
void RegisterRoutes(RouteCollection routes)
{
var sr = new ServiceRoute("Signature", new WebServiceHostFactory(), typeof(Signature));
sr.Url = "Services/Signature.svc/GetSignatureInfo";
routes.Add(sr);
}
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RegisterRoutes(RouteTable.Routes);
}
I get "NetworkError: 404 Not Found - _http://www.my-project.com/Signature". Where am I wrong or what ajax url should be ?!!! Please, help.
Thanks in advance.
My work colleague has found a solution:
The problem was in IIS configuration -
If my app is under Default Site , I should add ajax url with prefix of project. For example, if my Service is located in Web project, I should enter url like : '/Web/Services/MyService/MyFunction' in js file. The same url should be on Global.asax file, in ServiceRoute url, but without first '/'.
In this case, it will work fine locally,but there will be a problem to put it on production server.
The optimal solution:
1. IIS : add web site, configure physical path to him, change its port(!!!).
2. .js + Global.asax : just remove project name from url in both places. So, the url will be like : in js file '/Services/MyService/MyFunction' and in Global.asax 'Services/MyService/MyFunction' (without first '/')
That's all. Big thanks to Miri(colleague).

Execute some code for each request for ASP.NET (aspx and cshtml )

Is there a way to write some code that would be executed for each request to a .aspx or a .cshtml page in asp.net 4.5 apart from using a base page class. it is a very huge project and making changes to all pages to use a base page is a nightmare. Also i am not sure how would this be done for a cshtml page since they don't have a class.
Can we use the Application_BeginRequest and target only the aspx and cshtml files since the website is running in integrated mode.?
basically, i have to check if a user who is accessing the website has a specific ip address against a database and if yes then allow access otherwise redirect.
we are using IIS8 and ASP.Net 4.5 and ASP.Net Razor Web Pages
Also i am not sure how would this be done for a cshtml page since they don't have a class.
You could place a _ViewStart.cshtml file whose contents will get executed on each request.
Alternatively you could write a custom Http Module:
public class MyModule: IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(OnBeginRequest);
}
public void Dispose()
{
}
public void OnBeginRequest(object s, EventArgs e)
{
// this code here's gonna get executed on each request
}
}
and then simply register this module in your web.config:
<system.webServer>
<modules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</modules>
...
</system.webServer>
or if you are running in Classic Mode:
<system.web>
<httpModules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</httpModules>
</system.web>
basically, i have to check if a user who is accessing the website has
a specific ip address against a database and if yes then allow access
otherwise redirect.
Inside the OnBeginRequest method you could get the current user IP:
public void OnBeginRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
var request = app.Context.Request;
string ip = request.UserHostAddress;
// do your checks against the database
}
Asp.net MVC filters are especially designed for that purpose.
You would implement ActionFilterAttribute like this (maybe put this new class in a Filters folder in your webapp solution):
public class IpFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string ip = filterContext.HttpContext.Request.UserHostAddress;
if(!testIp(ip))
{
if (true /* You want to use a route name*/)
filterContext.Result = new RedirectToRouteResult("badIpRouteName");
else /* you want an url */
filterContext.Result = new RedirectResult("~/badIpController/badIpAction");
}
base.OnActionExecuting(filterContext);
}
private bool testIp(string inputIp)
{
return true /* do you ip test here */;
}
}
Then you have to decorate any action that would perform the ipcheck with IpFilter like so :
[IpFilter]
public ActionResult AnyActionWhichNeedsGoodIp()
{
/* do stuff */
}

Resources