angularjs - ASP.NET WebAPI2 CORS: null request in GetOwinContext on preflight -
angularjs - ASP.NET WebAPI2 CORS: null request in GetOwinContext on preflight -
i'm creating angularjs (typescript) spa webapi2 backend, requiring authentication , authorization api. api hosted on different server, i'm using cors, next guidance found @ http://www.codeproject.com/articles/742532/using-web-api-individual-user-account-plus-cors-en i'm newcomer in field.
all works fine, can register , login, , create requests restricted-access controller actions (here dummy "values" controller default vs webapi 2 template) passing received access token, in client-side service relevant code:
private buildheaders() { if (this.settings.token) { homecoming { "authorization": "bearer " + this.settings.token }; } homecoming undefined; } public getvalues(): ng.ipromise<string[]> { var deferred = this.$q.defer(); this.$http({ url: this.config.rooturl + "api/values", method: "get", headers: this.buildheaders(), }).success((data: string[]) => { deferred.resolve(data); }).error((data: any, status: any) => { deferred.reject(status.tostring() + " " + data.message + ": " + data.exceptionmessage); }); homecoming deferred.promise; }
now, i'd retrieve user's roles 1 time logged in angularjs app can behave accordingly. added method in business relationship api (which @ class level has attributes [authorize]
, [routeprefix("api/account")]
, [enablecors(origins: "*", headers: "*", methods: "*")]
(*
testing purposes):
[route("userroles")] public string[] getuserroles() { homecoming usermanager.getroles(user.identity.getuserid()).toarray(); }
i added code login controller:
private loaduserroles() { this.accountservice.getuserroles() .then((data: string[]) => { // store roles in app-settings service this.settings.roles = data; }, (reason) => { this.settings.roles = []; }); } public login() { if ((!this.$scope.name) || (!this.$scope.password)) return; this.accountservice.loginuser(this.$scope.name, this.$scope.password) .then((data: iloginresponsemodel) => { this.settings.token = data.access_token; // load roles here this.loaduserroles(); }, (reason) => { this.settings.token = null; this.settings.roles = []; }); }
where business relationship controller's method is:
public getuserroles() : ng.ipromise<string[]> { var deferred = this.$q.defer(); this.$http({ url: this.config.rooturl + "api/account/userroles", method: "get", headers: this.buildheaders() }).success((data: string[]) => { deferred.resolve(data); }).error((data: any, status: any) => { deferred.reject(status.tostring() + ": " + data.error + ": " + data.error_description); }); homecoming deferred.promise; }
anyway triggers options preflight request, in turn causes 500 error. if inspect response, can see getowincontext method gets null request. here origin of error stack trace:
{"message":"an error has occurred.","exceptionmessage":"value cannot null.\r\nparameter name: request","exceptiontype":"system.argumentnullexception","stacktrace":" @ system.net.http.owinhttprequestmessageextensions.getowincontext(httprequestmessage request)\r\n @ accounts.web.controllers.accountcontroller.get_usermanager() ...}
yet, code i'm using getting roles no different utilize getting dummy "values" webapi test controller. can't see reason why preflight should required here, in case i'm getting nasty exception in owin code.
my request header (the api beingness @ port 49592):
options /api/account/userroles http/1.1 host: localhost:49592 connection: keep-alive access-control-request-method: origin: http://localhost:64036 user-agent: mozilla/5.0 (windows nt 6.3; wow64) applewebkit/537.36 (khtml, gecko) chrome/35.0.1916.153 safari/537.36 access-control-request-headers: accept, authorization accept: */* referer: http://localhost:64036/ accept-encoding: gzip,deflate,sdch accept-language: en-us,en;q=0.8,it;q=-5.4
could explain?
i think found sort of working solution if looks dirty, @ to the lowest degree works. i'm posting here other can take advantage of it, i'm open suggestions. (sorry bad formatting, tried several times , editor not allow me correctly mark code).
essentially, solution suggested reply post: handling cors preflight requests asp.net mvc actions, changed code did not work me (webapi 2 , .net 4.5.1). here it:
in global.asax
, method application_start
, add together beginrequest += application_beginrequest;
.
add override, responds options
requests allowing (this ok in testing environment):
protected void application_beginrequest(object sender, eventargs e) { if ((request.headers.allkeys.contains("origin")) && (request.httpmethod == "options")) { response.statuscode = 200; response.headers.add("access-control-allow-origin", "*"); response.headers.add("access-control-allow-methods", "get, put, post, delete");
string srequestedheaders = string.join(", ", request.headers.getvalues("access-control-request-headers") ?? new string[0]); if (!string.isnullorempty(srequestedheaders)) response.headers.add("access-control-allow-headers", srequestedheaders); response.end();
} }
the attribute decorating accounts controller method routeattribute
:
[route("userroles")] public string[] getuserroles() { string id = user.identity.getuserid(); debug.assert(id != null); string[] aroles = usermanager.getroles(id).toarray(); homecoming aroles; }
this way options
request gets proper response , successive succeeds.
addition
i must add together enablecors attribute not plenty must not handle options
verb, ensure cors request gets access-control-allow-origin
header. otherwise, might observe apparently right response (code 200 etc) see $http
phone call failing. in case add together global.asax
line:
globalconfiguration.configuration.messagehandlers.add(new corsalloworiginhandler());
my corsalloworiginhandler
delegatinghandler
ensures header value *
nowadays in each response request included origin
header:
public sealed class corsalloworiginhandler : delegatinghandler { protected async override task<httpresponsemessage> sendasync (httprequestmessage request, cancellationtoken cancellationtoken) { httpresponsemessage response = await base.sendasync(request, cancellationtoken); // cors-related headers must contain access-control-allow-origin header, // or request fail. value may echo origin request header, // or `*`. if ((request.headers.any(h => h.key == "origin")) && (response.headers.all(h => h.key != "access-control-allow-origin"))) { response.headers.add("access-control-allow-origin", "*"); } homecoming response; } }
angularjs cors owin asp.net-web-api2
Comments
Post a Comment