Web api code - Failed to load - Access Control Allow Origin - c#

My website is running fine. Basically I am having some html pages that calls a webapi to get the results and bind the chart. The api only sends the json data and rest is done on html page in script tag.
Now I have to integrate some of my website pages into another website so I have created the html pages but when I run them I am getting Error - 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
The mvc code:
[HttpGet, Route("DevD"), OutputCache(Duration = 5, VaryByHeader = "Origin")]
public ActionResult GetDevD(double value1)
{
return Json(_devD, JsonRequestBehavior.AllowGet);
}
private static object _devD;
private static object GetDevD()
{
dynamic trend = Trend;
return new
{
trend?.day,
volume = new abc(GetLastTick("JZ"))
};
}
In global.ascx
protected void Application_BeginRequest()
{
var origin = Request.Headers["Origin"];
if (!string.IsNullOrWhiteSpace(origin) && (origin.EndsWith("abc.com") || origin.EndsWith("www.watrade.net")))
{
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "accept, content-type");
}
if (Request.HttpMethod != "OPTIONS") return;
Response.End();
}
I don't want to use any plugin. What changes should I made in this code so that it runs in my website also and can be integrated in another website also.

Related

Custom authorize attribute doesn't work after deploying to IIS

I have overridden the HandleUnauthorizedRequest method in my asp.net mvc application to ensure it sends a 401 response to unauthorized ajax calls instead of redirecting to login page. This works perfectly fine when I run it locally, but my overridden method doesn't get called once I deploy to IIS. The debug point doesn't hit my method at all and straight away gets redirected to the login page.
This is my code:
public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.Result = new JsonResult
{
Data = new
{
success = false,
resultMessage = "Errors"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.HttpContext.Response.End();
base.HandleUnauthorizedRequest(filterContext);
}
else
{
var url = HttpContext.Current.Request.Url.AbsoluteUri;
url = HttpUtility.UrlEncode(url);
filterContext.Result = new RedirectResult(ConfigurationManager.AppSettings["LoginUrl"] + "?ReturnUrl=" + url);
}
}
}
and I have the attribute [AjaxAuthorize] declared on top of my controller. What could be different once it's deployed to IIS?
Update:
Here's how I'm testing, it's very simple, doesn't even matter whether it's an ajax request or a simple page refresh after the login session has expired -
I deploy the site onto my local IIS
Login to the website, go to the home page - "/Home"
Right click on the "Logout" link, "Open in a new tab" - This ensures that the home page is still open on the current tab while
the session is logged out.
Refresh Home page. Now here, the debug point should hit my overridden HandleUnauthorizedRequest method and go through the
if/else condition and then redirect me to login page. But it
doesn't! it just simply redirects to login page straight away. I'm
thinking it's not even considering my custom authorize attribute.
When I run the site from visual studio however, everything works fine, the control enters the debug point in my overridden method and goes through the if/else condition.
When you deploy your web site to IIS, it will run under IIS integrated mode by default. This is usually the best option. But it also means that the HTTP request/response model isn't completely initialized during the authorization check. I suspect this is causing IsAjaxRequest() to always return false when your application is hosted on IIS.
Also, the default HandleUnauthorizedRequest implementation looks like this:
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
filterContext.Result = new HttpUnauthorizedResult();
}
Effectively, by calling base.HandleUnauthorizedRequest(context) you are overwriting the JsonResult instance that you are setting with the default HttpUnauthorizedResult instance.
There is a reason why these are called filters. They are meant for filtering requests that go into a piece of logic, not for actually executing that piece of logic. The handler (ActionResult derived class) is supposed to do the work.
To accomplish this, you need to build a separate handler so the logic that the filter executes waits until after HttpContext is fully initialized.
public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new AjaxHandler();
}
}
public class AjaxHandler : JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
var httpContext = context.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
if (request.IsAjaxRequest())
{
response.StatusCode = (int)HttpStatusCode.Unauthorized;
this.Data = new
{
success = false,
resultMessage = "Errors"
};
this.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
base.ExecuteResult(context);
}
else
{
var url = request.Url.AbsoluteUri;
url = HttpUtility.UrlEncode(url);
url = ConfigurationManager.AppSettings["LoginUrl"] + "?ReturnUrl=" + url;
var redirectResult = new RedirectResult(url);
redirectResult.ExecuteResult(context);
}
}
}
NOTE: The above code is untested. But this should get you moving in the right direction.

Pass header cookies from Web API post to header location page

I have an ASP.Net Web API that is posted to from an external source. The values that are posted to the web api are used to determine the user's rights on our website. So the web api then passes the result object of my business logic as a bunch of cookies to our asp landing page. The problem is that the cookies are no longer available in the web page that the web api routed the response to.
Here is web api:
[HttpPost]
public HttpResponseMessage Reports(ReportRequest reportRequest)
{
if (reportRequest != null)
{
var reportAccess = new SwitchBL().CheckUserAccess(reportRequest);
var response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri(BaseUrl() + "/menu.aspx");
var json = JsonConvert.SerializeObject(reportAccess);
Dictionary<string, string> biscuitTin = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
foreach (var biscuit in biscuitTin)
{
var cookie =
new CookieHeaderValue(biscuit.Key, biscuit.Value ?? "")
{
Expires = DateTimeOffset.Now.AddDays(1),
Domain = Request.RequestUri.Host == "localhost" ? null : Request.RequestUri.Host,
HttpOnly = true
};
//cookierJar.Add(cookie);
response.Headers.AddCookies(new CookieHeaderValue[] {cookie} );
}
return response;
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
And so far my very simple aspx page always shows count = 0:
public partial class menu : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var cookieCount = Request.Cookies.Count;
}
}
The web api and aspx pages are in the same project thus hosted in one site. I do not want to use session variables and do not want to pass values in querystrings. Is there another way of passing data to the routed page from the web api or am I missing something here?
BTW, if I post to the api using Postman, the cookies are visible in the response header of the web api, so cookies are created. If I post using another web page, using Fiddler, I can see the cookies in the response of the api but then there are no cookies in the (receiving) asp page.
UPDATE
Thanks to the answer of Kai, I can now get the cookies in my route asp page as set in response.Headers.Location. I have a breakpoint in that page so I know it is being hit and cookie count is now as expected. However, the browser does not render the routed page. It remains on the original posting page. Here is the code I'm using in my post emulator page to call the web api:
protected void Page_Load(object sender, EventArgs e)
{
}
protected async void DoIt_OnClick(object sender, EventArgs e)
{
var reportRequest = new ReportRequest();
reportRequest.EmailAddress = Email.Text;
reportRequest.UserNumber = UserCode.Text;
reportRequest.MobileNumber = MobileNumber.Text;
reportRequest.Password = Password.Text;
reportRequest.Country = Country.Text;
reportRequest.AccountNumber = AccountNumber.Text;
reportRequest.AccountType = AccountType.Text;
reportRequest.ReportType = ReportType.Text == "" ? 0 : Convert.ToInt32(ReportType.Text);
reportRequest.PhoneInfo = PhoneInfo.Text;
await GoThereAsync(reportRequest);
}
public async Task<Uri> GoThereAsync(ReportRequest reportRequest)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:7789/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
var response = await client.PostAsJsonAsync<ReportRequest>("api/switch/reports", reportRequest);
if (response.IsSuccessStatusCode)
{
return response.RequestMessage.RequestUri;
}
return null;
}
}
To summarise: Emulator.aspx does POST to web api. Web API sets cookies and location to home.aspx. Home.aspx receives cookies (debug steps into code-behind) but browser remains on Emulat.aspx and does not render home.aspx.
Try CookieHeaderValue.Path = "/".
From the Microsoft Docs:
Path: Restricts the cookie to the specified path within the domain. If not specified, the path of the request URI is used.

"An item with the same key has already been added." while adding "/&output=embed"

Implementing a MVC application in C# with Evernote API. I am using the AsyncOAuth.Evernote.Simple nuget package. Receiving and error of Refused to display in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN', when trying to navigate to URL that fires off the OAuth process.
There is an iframe that is surrounding my code (which can not be altered). After implementing the code an error is generated: "An item with the same key has already been added". This error occurs when requestToken is hit for the first time.
Below is my EvernoteProviderController.cs
public class EvernoteProviderController : Controller
{
// Initialize Oauth call, pulling values from web.config
EvernoteAuthorizer EvernoteAuthorizer = new EvernoteAuthorizer(ConfigurationManager.AppSettings["Evernote.Url"] + "&output=embed", ConfigurationManager.AppSettings["Evernote.Key"], ConfigurationManager.AppSettings["Evernote.Secret"]);
// This method makes the original call to Evernote to get a token so that the user can validate that they want to access this site.
public ActionResult Authorize(bool reauth = false)
{
// Allow for reauth
if (reauth)
SessionHelper.Clear();
// First of all, check to see if the user is already registered, in which case tell them that
if (SessionHelper.EvernoteCredentials != null)
return Redirect(Url.Action("AlreadyAuthorized"));
// Evernote will redirect the user to this URL once they have authorized your application
var callBackUrl = Request.Url.GetLeftPart(UriPartial.Authority) + Url.Action("ObtainTokenCredentials");
// Generate a request token - this needs to be persisted till the callback
var requestToken = EvernoteAuthorizer.GetRequestToken(callBackUrl);
// Persist the token
SessionHelper.RequestToken = requestToken;
// Redirect the user to Evernote so they can authorize the app
var callForwardUrl = EvernoteAuthorizer.BuildAuthorizeUrl(requestToken);
return Redirect(callForwardUrl);
}
// This action is the callback that Evernote will redirect to after the call to Authorize above
public ActionResult ObtainTokenCredentials(string oauth_verifier)
{
// Use the verifier to get all the user details we need and store them in EvernoteCredentials
var credentials = EvernoteAuthorizer.ParseAccessToken(oauth_verifier, SessionHelper.RequestToken);
if (credentials != null)
{
SessionHelper.EvernoteCredentials = credentials;
return Redirect(Url.Action("Authorized"));
}
else
{
return Redirect(Url.Action("Unauthorized"));
}
}
// Show the user if they are authorized
public ActionResult Authorized()
{
return View(SessionHelper.EvernoteCredentials);
}
public ActionResult Unauthorized()
{
return View();
}
//Redirects user if already authorized, then dump out the EvernoteCredentials object
public ActionResult AlreadyAuthorized()
{
return View(SessionHelper.EvernoteCredentials);
}
public ActionResult Settings()
{
return View();
}
}
Has anyone had this issue with iframes before or knows in what direction I should go? I am trying to embed my URL endpoint so I can get around the iframe error.
Solved the error.
A bit of back story:
The purpose of this application was to provide the OAuth page where a user can sign up which will generate a AuthToken and NotebookURL, (both are needed with Evernote API to pull read/write Notes - which is Evernote's object).
The previous behavior (before I changed it), was when a user clicked on the link - they will be redirected (in the same window) to the Evernote OAuth page.
This caused issues for me, because I had another wrapper around my code (iframe). So in non-technical terms, I had a iframe within an iframe within an iframe.
Workaround
Created a JavaScript code which would add an click event listener, which would then create a popup using window.open.
$("#btnStart").click(function () {
myWindow = window.open(baseUrl + "/EvernoteProvider/Authorize", '_blank', 'width=500,height=500, scrollbars=no,resizable=no');
myWindow.focus();
});

asp.net web api server side talking back to angularjs

I am developing web site in angular js, .net web api 2, visual studio 2013, and c#.
My url navigation is purely driven by angularjs.
Upon password reset, I email a link to the user that looks like this:
http://localhost:3458/api/Account/ResetPasswordEmail?userId=xxxxxxxxxxxxxxxx9049a8e3-fb90-400a-b73e-78cadc16ae40&code=BVEssssssssssssssssssswT9FsJ3QncMLaPclhLZVUHpHifX8wmG7f3ZrlxDlwkkmcMNccdXz8jdEuGdHM1FJ4WdBu9Yxu9VG43DxamBrasdfasdfasdfbdasdfvgGrqwJxRoJ%2FSCDkOrbV3RupmUaoTgRmebwb1ymBZwkd891G3q6SW%2F%2FTDwOQ7qrkzkAUYtjcwd%2FTH4jNNCzIYmMXF%2BkMF26mBM4Osgc%2Bi%2BO0So41%2Fpp3yK%2BDvEtNCPA%3D%3D&newPassword=xxxxxxxxxx
User receives the email and clicks on the link..
On the server side, following code is executed:
//
// GET: /Account/ResetPasswordEmail
// GET api/Account/ResetPasswordEmail
[Route("ResetPasswordEmail", Name = "ResetPasswordEmail")]
[AllowAnonymous]
public HttpResponseMessage GetResetPasswordEmail(string userId, string code, string newPassword)
{
string errmsg = string.Empty;
try
{
if (userId == null || code == null)
{
errmsg = "Either email address or other necessary parameter is null.";
throw new Exception(errmsg);
}
var result = UserManager.ResetPasswordAsync(userId, code, newPassword);
var response = Request.CreateResponse(HttpStatusCode.Moved);
string fullyQualifiedUrl = Request.RequestUri.GetLeftPart(UriPartial.Authority);
response.Headers.Location = new Uri(fullyQualifiedUrl);
return response;
}
catch(Exception ex)
{
return null;
}
}
The password is reset successfully...
However ...
I want to give visual feedback on the web page that password was or was not reset successfully. In other words, in this situation how would my web api talk back to the angular js code to render a UI. Would I resort to some sort of asp.net mvc type of code?
In your website project's controller folder need to add action which where user will land from email
Then call api from that action if success then return success view in that action

ASP.NET request extension type

I am working on a large web application which I have recently shelved tons of .aspx pages from the project.
To avoid page not found error, I added these entities in the xml which came around 300+ in count. I wrote a http module that checks the request url in the xml entities and if they are found, my module is going to redirect the request to respective new pages.
Everything works great, but my collection is getting iterated for all the requests, I mean for each and every .jpg, .css, .js, .ico, .pdf etc.
Is there any object or property in .net that can tell the type of request that user requested for like HttpContext.request.type. So that I can avoid checking the request for all unwanted file types.
Yet another approach is to use ASP.NET Routing (from .NET 3.5) to create routes that map each of the old pages onto a handler for the new page. ASP.NET routing makes it easy to have multiple Urls for a single ASPX page and you can in fact hide .ASPX completely from the end-user and have SEO friendly Urls for all your pages instead. If you map multiple URLs onto one page you'll want to put the canonical URL tag on the page.
Alternatively
If you want redirects you can register routes with a simple redirect route handler like this:-
routes.Add(new Route("sample.aspx", new RedirectRouteHandler("/home/newsample.aspx")));
And the RedirectRouteHandler might look something like this:-
/// <summary>
/// Redirect Route Handler
/// </summary>
public class RedirectRouteHandler : IRouteHandler
{
private string newUrl;
public RedirectRouteHandler(string newUrl)
{
this.newUrl = newUrl;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new RedirectHandler(newUrl);
}
}
/// <summary>
/// <para>Redirecting MVC handler</para>
/// </summary>
public class RedirectHandler : IHttpHandler
{
private string newUrl;
public RedirectHandler(string newUrl)
{
this.newUrl = newUrl;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext httpContext)
{
httpContext.Response.Status = "301 Moved Permanently";
httpContext.Response.StatusCode = 301;
httpContext.Response.AppendHeader("Location", newUrl);
return;
}
}
You could use HttpContext.Current.Request.FilePath property which gets the virtual path of the current request.
For example, for the URL http://www.contoso.com/virdir/page.html/tail, the FilePath value is /virdir/page.html.
The second step is to get the extension itself. For example, you may do it using System.IO.Path.GetExtension method. For the /virdir/page.html path it'll return .html extension.
You could instead catch just the 404 errors to avoid intercepting every page request. Add an Application_Error method to your global.asax like below. This will allow you to also redirect to a special error page if the page isn't one you need to redirect according to your XML file.
protected void Application_Error(object sender, EventArgs e)
{
Log.Error("*** Application_Error ***");
Exception baseException = Server.GetLastError().GetBaseException();
HttpException httpException = baseException as HttpException;
if (httpException != null)
{
int httpCode = httpException.GetHttpCode();
Log.ErrorFormat("HTTPEXCEPTION: {0} : {1}", httpCode, HttpContext.Current.Request.RawUrl);
if (httpCode == 404)
{
...

Categories