When I make a $http.post from my client to the web api on a different project, firebug and chrome both show an OPTIONS method and then a POST method.
The problem
Both request actually execute my API action. If I restrict to POST, the OPTIONS fails and the post never happens. I have CORS turned on in both angular and webapi.
my app.js has these lines:
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
my api call:
$scope.add = function () {
var role = { Name: 'admin' };
$http.post('http://localhost:7514/Roles/add', role).
success(function () {
alert('RolesController.add');
}).
error(function (message) {
alert('FAILED EXECUTE RolesController.add');
});
};
the webapi web.config
<system.webServer>
...
<!-- added for cors handling, remove when using a dedicated cors handler or the system.web.cors dll-->
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*"/>
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
</system.webServer>
If you use the nightly CORS package, the OPTIONS request gets intercepted by a message handler which doesn't execute the action, but still sends the appropriate response header. For more information and how to use nightly packages, you should look at this blog post regarding getting the CORS nightly package.
http://blogs.msdn.com/b/yaohuang1/archive/2013/04/05/try-out-asp.net-web-api-cors-support-using-the-nightly-builds.aspx
Make sure to select "Prerelease" from the the drop down.
Related
In my application i am using web api with token based authentication with CORS support, but when client request for the token, an error occured due to CORS (Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at (my site name) . This can be fixed by moving the resource to the same domain or enabling CORS.)
I had configured everything required for CORS support ( i think so). here my configuration
Owin start up class
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration
{
DependencyResolver = new StructureMapWebApiDependencyResolver(container)
};
WebApiConfig.Register(config); // registering web api configuration
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); // cors for owin token pipeline
app.UseWebApi(config);
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthAuthorizationServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthAuthorizationServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
And my webapi configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors(); // Corse support for Web api
config.MapHttpAttributeRoutes(); // attribute based urls
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
here configuration in web.config
<system.webserver>
<httpProtocol>
<customHeaders>
<!-- Adding the following custom HttpHeader will help prevent CORS from stopping the Request-->
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, DELETE" />
</customHeaders>
</httpProtocol>
</system.webserver>
and my request header from mozilla
Accept application/json, text/plain, */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Content-Length 67
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Host talenterp
Origin http://192.168.1.11:85
Referer http://192.168.1.11:85/
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0
The URLs of Apps are
Server app (which should support CORS)
{http://talenterp}
Token end point :
{http://talenterp/token}
Client app
{http://talentmvc:85}
NB: I already added
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
in GrantResourceOwnerCredentials() method of my AuthorizationServerProvider
Be sure you've got only
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
configured, and not also the old style 'config.EnableCors()' in your Global.asax or WebApiConfig. Furthermore: place the above statement as the first one in your owin Startup class. Yes that really makes a difference, setting it later can also cause cors to not work.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
... etc
OWIN and Microsoft.AspNet.WebApi.Cors are two separate libraries and each one needs separate configuration.
Disable use of CORS with OWIN:
public void Configuration(IAppBuilder app)
{
//app.UseCors(CorsOptions.AllowAll);
Find GrantResourceOwnerCredentials method and add Access-Control-Allow-Origin to context so when it returns a call after authentication is completed that browser finds the header and accepts it.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost" });
Now install Microsoft.AspNet.WebApi.Cors package from Nuget to your webapi project, and add this to Register method
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("http://localhost, ", "accept,accesstoken,authorization,cache-control,pragma,content-type,origin", "GET,PUT,POST,DELETE,TRACE,HEAD,OPTIONS");
config.EnableCors(cors);
This did it for me.
Especially if you are having problem with the Web API bearer token when using CORS then dont forget to put "TOKEN" in the list of your allowed methods.
Please put the code in your system.webServer of web.config, that is how i solved mine
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, DELETE, TOKEN" />
</customHeaders>
Had the same problem. In addition to the above indications (using app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll) only, and setting it up as first thing), I had to specify the following in the application Web.config file to be able to handle Option Requests:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
</system.webServer>
Due to some headers that I was sending in the authentication request, an Options request is sent before the actual POST request, and it needs to return the correct 'Access-Control-Allow-Origin' header before the POST is sent.
If none of the CORS headers are returned by the options response, then the POST will not be sent at all. The added configuration enables this behaviour as well as for Trace.
As explained in this post
I had this similar problem, I tried all the options above in startup.cs i added app.UseCors(CorsOptions.AllowAll); at the top and in the WebApiConfig i disabled
public static void Register(HttpConfiguration config)
{
//var cors = new EnableCorsAttribute("*", "*", "*");
//config.EnableCors(cors);
}
and also disabled cors in
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
}
And repeated this in reverse order and also included entries in web.config but all did not work.
When I begun asking myself why is app.UseWebApi(config); not accessible yet I have seen it work somewhere. I looked around and found out installing Install-Package Microsoft.AspNet.WebApi.OwinSelfHost fixes it.
Eventually, it fixed the whole problem, though app.UseCors(CorsOptions.AllowAll) has to be placed first in the startup.cs method. In fact without app.UseWebApi(config), I tested in postman and the end points actually didn't exist. Overall it's working pretty well for me now.
On a webserver with Kerberos the client will send a request anonymously, and get a 401 status back. Then it sends the same request again, with authentication, and now get a 200 status back. Is it possible to set up a web application in MVC/JavaScript/etc to know that an anonymous request is futile and go stright for the user authentication request? For a specific case I am using jQuery and AJAX that is pulling data from a server at short intervals.
UPDATE: I want the client to know that there is no use sending an anonymous request, so it can just as well send a request with a username the first time. Why sending an anonymous request in the first place when you absolutely know you will only get a 401 back?
Based on this answer, you should just use beforeSend callback and then add the Authorization header on your own.
You are looking for preemptive authentication and this is highly discouraged. Do not send credentials unless the server challenges you otherwise you may reveal secrets to an unknown server.
update as you don't need to allow anonymous access.
You could remove default IIS authentication module and/or add your own custom HttpModule for a specific part
<location path="PathToWebApi">
<system.web>
<httpModules>
<!-- default IIS HttpModules -->
<remove name="WindowsAuthentication"/>
<remove name="FormsAuthentication"/>
<remove name="PassportAuthentication"/>
<remove name="RoleManager"/>
<remove name="UrlAuthorization"/>
<remove name="FileAuthorization"/>
<remove name="AnonymousIdentification"/>
<remove name="Profile"/>
<add name="CustomAuthentication" type="Your.NameSpace.CustomAuthentication"/>
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="false">
<add name="CustomAuthentication" type="Your.NameSpace.CustomAuthentication" preCondition="managedHandler"/>
</modules>
</location>
You can implement CustomAuthentication : IHttpModule class that inspect incoming request context and set current user identity depend on your custom logic.
public void Init(HttpApplication context)
{
//add event listener to authenticate Http request
//context.AuthenticateRequest += new EventHandler(AuthenticateRequest); //Session is null at AuthenticateRequest state
context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);
}
I have my own custom server to expose data from an XML file. I can browse through it in whichever browser of my choosing and I can query the data in Fiddler, but Jaydata (or one of its building blocks) can't seem to grab the same data. What's most frustrating is that my code is (or was, I've tweaked it slightly to try and resolve these errors) pretty much an exact duplicate of code found here and here. Here's my script:
<script type="text/javascript" src="/Scripts/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="/Scripts/datajs-1.0.3.js"></script>
<script type="text/javascript" src="/Scripts/jaydata.js"></script>
<script type="text/javascript" src="/Scripts/jaydataproviders/oDataProvider.js"></script>
<script type="text/javascript" src="/Scripts/Block.js"></script>
<script type="text/javascript">
var Context = new foo({
name: 'oData',
oDataServiceHost: 'http://localhost:xxx'
});
function createItemLI(user, id, css) {
var li = "<li></li>".append(name).addClass(css).data('id', id);
return li;
}
$.when($.ready, Context.onReady()).then(function () {
Context.Roots.toArray(function (roots) {
roots.forEach(function (root) {
$('#roots').append(
createItemLI(root.User, root.ID, 'root'));
});
});
});
</script>
Block.js, is the file generated by JaySvcUtil.exe
There's only one thing in the body of the .htm file, a simple <ul id="roots"></ul>
When I try to run the project, there's nothing on the page. When I used FireBug, I get "HTTP request failed" The requestUri is http://localhost:xxx/Roots, which works when I manually browse to it, but the StatusCode is 0, statusText is the empty string, and so on and so forth. I've looked at Fiddler, at it gets exactly what I expected.
I'm assuming there's some manner of flag that needs to be set, but none of the tutorials I've found have been of any help. It's assumed that it works out of the box, and I too had high expectations getting simple read access would be easy.
UPDATE:
it turns out Internet Explorer has been receiving the appropriate data as JSON, though it still doesn't populate the roots as it should. In FireFox it returns a "501 not implemented" error, because my GET request is being altered to be OPTION. I don't have a web.config file as would a project I started as a WCF service. This is just a console app in Visual Studio 2010. So I guess my question becomes "how do I better specify cross-domain behavior through JayData?"
Try This:
var oProviderConfig = {
name: 'oData',
oDataServiceHost: 'http://localhost:xxx/Roots/'
,enableJSONP: false
};
Also you have to enable CORS support from your webservice. If you are using .NET
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Max-Age" value="3600" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion" />
<add name="Access-Control-Allow-Methods" value="PUT, POST, GET, DELETE, MERGE, OPTIONS" />
</customHeaders>
</httpProtocol>
OPTIONS http://localhost:7514/Employees/testrestricted 401 (Unauthorized) angular.js:10419
OPTIONS http://localhost:7514/Employees/testrestricted Origin http://localhost:4064 is not allowed by Access-Control-Allow-Origin. angular.js:10419
XMLHttpRequest cannot load http://localhost:7514/Employees/testrestricted. Origin http://localhost:4064 is not allowed by Access-Control-Allow-Origin.
I have my app.js setup like this already:
var app = angular.module('angular-auth-demo', ['http-auth-interceptor']);
app.config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$routeProvider.
when('/home', { templateUrl: 'partial-content.html', controller: 'ContentController' }).
otherwise({ redirectTo: '/home' });
}]);
is there a way to find out if this is an error on angular or asp.net mvc, because I have a cors configuration on that end too, but i don't think the browser is actually getting a chance to hit the server?
I had a similar issue using Font Awesome cross domain - specifically with Firefox.
Adding a web.config in the directly with the resource you need to access with this solved it for me.
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
The request is getting to the server because the server is returning a 401. See the network tab in Chrome dev tools or Firebug. Assuming you have the Access-Control-Allow-Origin header already, you probably need to specify the Access-Control-Allow-Headers header and add Content-Type as its value. Maybe post your CORS config/code?
following solution worked for be:
http://forums.asp.net/t/1885459.aspx
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (this.Context.Request.Path.Contains("signalr/negotiate"))
{
this.Context.Response.AddHeader("Access-Control-Allow-Origin", "*");
this.Context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
this.Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
this.Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
}
}
I've read every post about this issue, but nothing solved the problem.
I'll be glad if someone can help me with that.
There's an MVC3 project with a web service that I added.
I have only one function called Test, and when I call it through an HTTP GET method (regular url) , it returns the data with XML format instead of JSON.
How can I make it return JSON?
The web service:
namespace TestServer
{
[WebService]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class TestWebservice : System.Web.Services.WebService
{
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
[WebMethod]
public List<string> Test()
{
return new List<string>
{
{"Test1"},
{"Test2"}
};
}
}
}
The web.config (only relevant parts):
<configuration>
<location path="TestWebservice.asmx">
<system.web>
<webServices>
<protocols>
<add name="HttpGet"/>
</protocols>
</webServices>
</system.web>
</location>
<system.web>
<webServices>
<protocols>
<clear/>
</protocols>
</webServices>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx"
type="System.Web.Script.Services.ScriptHandlerFactory"
validate="false"/>
</httpHandlers>
</system.web>
</configuration>
The url:
http://localhost:49740/testwebservice.asmx/Test
The result (which is not what I want):
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<string>Test1</string>
<string>Test2</string>
</ArrayOfString>
I'll be glad if someone can help me.
You need to specify the content type HTTP header to application/json when sending the request. For example if you are using jQuery AJAX you could do the following:
$.ajax({
url: '/testwebservice.asmx/Test',
type: 'GET',
contentType: 'application/json',
success: function(result) {
alert(result.d[0]);
}
});
Also you need to enable the GET verb on your [ScriptMethod] attribute:
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]
[WebMethod]
public List<string> Test()
{
return new List<string>
{
{"Test1"},
{"Test2"}
};
}
You could also get rid of everything you put in your web.config about this service. It's not necessary.
Oh and by the way, classic ASMX web services is an obsolete technology. You should use more recent technologies such as ASP.NET MVC controller actions returning JSON, WCF, or even the bleeding edge ASP.NET MVC 4 Web API.
The REST service serialize the data in particular format(XML, JSON) based upon the Accept header send from the client. It is the Accept header that says to the service which formats the client can accept.
When you trying to access the service directly from the browser URL the value of the Accept header is set to some default value as below (in firefox)
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
The above header explicitly says that I can accept html, xhtml or xml. Since the application/xml format is explicitly specified but not application/json the REST service returns the data in xml format. (I don't understand what is the use of ResponseFormat = ResponseFormat.Json though).
So if you want to make the service return JSON data you have to specify the accept header the corresponding format. If you are using jQuery you can utilize the $.getJSON() that will set the accept header as "application/json" or you can even use $.ajax with dataType as json.
http://prideparrot.com/blog/archive/2011/9/returning_json_from_wcfwebapi