Angular POST request's header is null - asp.net-web-api

I am developing an Ionic 3 Mobile Application, I have problem with Angular's POST method.
In login page, I created a form and tried send data to server with Angular HTTP POST method. But in server (.NET WEB API) I see request's header is null.
Here is the Angular side codes;
login(username, password):Observable<Object>{
let url : string = this.apiUrl+"/login";
let headers = new HttpHeaders();
headers.append('Authorization', btoa(username+":"+password).toString());
return this.http.post(url,JSON.stringify({username,password}), {headers: headers});
}
Here is the .NET side codes for controller;
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
public Response Post()
{
return _mobileUserService.Login();
}
Here is the part of .NET side codes for catch request;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var token = request.Headers.GetValues("Authorization").FirstOrDefault();
}
catch (Exception ex)
{
}
return base.SendAsync(request, cancellationToken);
}
When request catched by .NET (in running), I see these values for "request" variable;
request = {Method: POST, RequestUri: 'http://localhost:41582/api/login', Version: 1.1, Content: System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent, Headers:
{
Connection: keep-alive
Accept: application/json
Accept: text/plain
Accept: */*
...
In normally, request's url is localhost:8100, so I think server accepted CORS
How can I solve that?

In Web api you have to tell which method is post or get based on how you have setup your route.
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
[HttpPost] // Decorate post this attribute in your controller
public Response Post()
{
return _mobileUserService.Login();
}

Related

Axios authentifaction request fails to send header to Spring Boot

I am trying to implement Authorization with Axios on RN part and send the token to Spring Boot backend. I've tried to do it before with simple sending email and password as parameters of GET request and it worked fine, but now when I'am trying to send basic headers with btoa to backend part, it keeps receiving null value.
My React Native part:
login(user) {
const headers = {
authorization: 'Basic ' + btoa(user.email + ':' + user.password)
};
return axios.get(API_URL + 'login', {headers: headers})
.then(response => {
console.log('function called')
And my Controller on Spring Boot:
#RequestMapping(value = {"/login"}, method = RequestMethod.GET)
public ResponseEntity<?> login(Principal principal) {
if(principal == null){
//logout will also use here so we should return ok http status.
return (ResponseEntity<?>) ResponseEntity.badRequest();
}
UsernamePasswordAuthenticationToken authenticationToken =
(UsernamePasswordAuthenticationToken) principal;
When I debug the controller, I see that my method parameter principle is receiving null.
I guess the issue might be somewhere either in the header or Controller parameter, but have real idea.
Headers should be:
{
headers: {
"Authorization": "Basic "...
}
}
Alternatively,
{
auth: {
username: "",
password: ""
}
}
Which will add the basic auth header for you
https://github.com/axios/axios#request-config for reference

How to set authorization header in vue.js

I'm making an axios post call with the JWT token generated after successful login. For all the requests I need to attach JWT token in header and in the back-end which is developed on spring -boot I have logic to get the token from header and validate it.
From the browser, first the OPTIONS request goes to back-end where it gives me 403 error and in the back-end If I sysout headers, I can't find the header name X-XSRF-TOKEN
axios.post("http://localhost:8004/api/v1/auth", { "username": "test", "password" : "test"})
.then((response) => {
let token = response.data.token;
axios.defaults.headers.common["X-XSRF-TOKEN"] = token;
axios.post("http://localhost:8004/api/v1/getdata", {"action" : "dashboard"})
.then((response) => {
console.log(response.data);
}, (error) => {
console.log(error);
})
}, (error) => {
console.log(error);
})
Spring boot part
#Controller
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RequestMapping(path = "/api/v1")
public class ApplicationController {
#PostMapping(path = "/getdata")
#ResponseBody
public SessionData getData(#RequestBody ProfileRequest profileRequest) {
try {
return profileService.getData(profileRequest);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Setting Authorization Header is not something to do with vue, but it
is something to do with axios.
axios.post("http://localhost:8004/api/v1/getdata", {"action" : "dashboard"}, {
headers: {
Authorization: 'Bearer ' + token,
}
})
When you get the auth token you can configure the axios instance with:
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
common means applying the header to every subsequent request, while you can also use other HTTP verb names if you want to apply a header to only one request type:
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
You will find more info in https://github.com/axios/axios#config-defaults
...
axios.post('http://localhost:8004/api/v1/auth',
{ "username": "test", "password" : "test"}, {headers: { X-XSRF-TOKEN: `${token}`}})
...
the issue might not be axios but the cors policy set by spring security.
If you are using spring security check out this answer
I had the same issue, that answer helped solve my problem.

Enabling WebAPI CORS for Angular 2 authentification

I've seen a few answers on stackoverflow and I'm lost.
I have webapi 2 + standalone angular 2
webapi project is from template. the only thing i've changed is that i added CORS
and following line to IdentityConfig.cs > ApplicationUserManager Create()
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:3000" });
here I've all standard from template:
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
On the client side I have function to get access token, that works properly:
authenticate(loginInfo: Login): boolean {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post(this.baseUrl + 'Token', 'grant_type=password&username=alice2#example.com&password=Password2!',
{
headers: headers
})
.subscribe(
data => this.saveAuthToken(<AccessToken>(data.json())),
err => this.handleError(err),
() => console.log('authentication Complete')
);
return true;
}
And get function, that works ok without authentication (commented code) :
get(url: string) {
var jwt = sessionStorage.getItem(this.idTokenName);
var authHeader = new Headers();
if (jwt) {
authHeader.append('Authorization', 'Bearer ' + jwt);
}
return this.http.get(this.apiUrl + url, {
headers: authHeader
})
.map(res => res.json())
.catch(this.handleError);
//return this.http.get(this.apiUrl + url)
// .map(res => res.json())
// .catch(this.handleError);
}
But when i try to add Authorization header server returns:
XMLHttpRequest cannot load http://localhost:3868/api/values. Response for preflight has invalid HTTP status code 405
How to allow user to authenticate through Angular properly?
Install-Package Microsoft.Owin.Cors
Add to App_Start > Startup.Auth.cs > ConfigureAuth(IAppBuilder app)
app.UseCors(CorsOptions.AllowAll);
Only one line. That's all.
You could explicitly add the needed headers and methods:
context.Response.Headers.Add(
"Access-Control-Allow-Headers",
new[] { "Content-Type, Authorization" }
);
context.Response.Headers.Add(
"Access-Control-Allow-Methods",
new[] { "GET, POST, OPTIONS" }
);
I had to add the following to the globalasax.cs:
protected void Application_BeginRequest()
{
var req = HttpContext.Current.Request;
var res = HttpContext.Current.Response;
var val = res.Headers.GetValues("Access-Control-Allow-Origin");
if (val == null)
{
if (!req.Url.ToString().ToLower().Contains("token") || (req.Url.ToString().ToLower().Contains("token") && req.HttpMethod == "OPTIONS"))
{
res.AppendHeader("Access-Control-Allow-Origin", "http://localhost:4200");
}
}
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
res.StatusCode = 200;
res.End();
}
}
When talking to webapi angular and using a http post that either contains non-standard body contents (i.e json) or authentication then a pre-flight request is set that basically says 'am i okay to send the actual request'. Now there are several ways around this that essentially involve short cuts - use IE (if the server is on the same machine as IE ignores the port when deciding what the same machine is) or open CORS up to permit all (which is dangerous as the granting permission to an authenticated user opens your system up to all manner of hacks).
Anyway the solution we used was to add a method to the Globals.asax.cs on the server
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
var origin = HttpContext.Current.Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "content-type, withcredentials, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
Response.Flush();
}
}
Now the above is checking for the pre-flight very specifically and if it finds it it adds permissions to send the next request. On your system you may need to tweek the Allow_Headers request (easiest way is to use your browser f12 to look at what headers your pre-flight request is actually sending out.
Note that the above just deals with the pre-flight CORS will still apply for the actual http POST which will need correctly handling. For this we added the server we wanted to allow in to settings and then added the System.Web.Http.Cors to the WebApiConfig Register method as follows
var cors = new EnableCorsAttribute(Properties.Settings.Default.CORSOriginPermittedSite, "*", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
cors.SupportsCredentials = true;
config.EnableCors(cors);
This avoids hard coding the site which a production system really wants to avoid.
Anyway hopefully that will help.

WebAPI OData v4 custom action without parameters can't be routed with error "No routing convention was found..."

I have very simple OData controller that successfully process standard actions (at least GET, POST, PUT and DELETE methods are working). I have followed this tutorial and added simple bound action. The method has parameters argument, but actually it does not required the parameters:
[HttpPost]
public IHttpActionResult Close([FromODataUri] int key, ODataActionParameters parameters) {
return Ok();
}
I have defined this action in OData EDM configuration as following:
builder.EntitySet<Ticket>("tickets");
builder.EntityType<Ticket>().Action("Close");
I am trying to call action from Postman:
POST /odata/tickets(2)/Default.Close HTTP/1.1
Host: localhost:50477
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: eef4c1f6-8c7f-f5eb-c22d-4397f3bda170
But receives the error message:
{
"error": {
"code": "",
"message": "No HTTP resource was found that matches the request URI 'http://localhost:50477/odata/tickets(2)/default.close'.",
"innererror": {
"message": "No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.",
"type": "",
"stacktrace": ""
}
}
}
I have read the whole internet and all related articles on SO but can't fix this issue. Please help me because I have no any fresh idea how to fight this.
My controller:
public class TicketsController : ODataController
{
[HttpPost]
public IHttpActionResult Close([FromODataUri] int key, ODataActionParameters parameters)
{
return Ok();
}
}
My request:
string requestUri = "http://localhost/odata/tickets(2)/Default.Close";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
request.Content = new StringContent("",
Encoding.UTF8,
"application/json");
HttpResponseMessage response = _client.SendAsync(request).Result;
Or remove the ODataActionParameters parameters in the close method and call with:
string requestUri = "http://localhost/odata/tickets(2)/Default.Close";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
HttpResponseMessage response = _client.SendAsync(request).Result;
My EdmModel is use your model.

Cross domain put call does not work with Access-Control-Allow-Origin

I am facing problem related to cross domain PUT call , i have allowed Access-Control-Allow-Origin from server side put still it doesn't work.
#PUT
#Path("/getresponse/{caller}")
#Produces({MediaType.APPLICATION_JSON})
public Response getResponseData(#PathParam("caller") String caller ,#QueryParam("ticket")String ticket ,#FormParam("formParam") String data){
ResponseBuilder resp;
System.out.println("name of caller is -> "+ caller);
System.out.println("query param ticket -> "+ ticket);
System.out.println("form param data->" + data);
Employee emp = new Employee();
emp.setAge(23);
emp.setName("data");
Gson gson = new Gson();
String responseJson = gson.toJson(emp);
resp=Response.ok(responseJson);//header("Access-Control-Allow-Origin", "*")
resp.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
return resp.build();
}
whenever i call it from jquery ajax method it says
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
I have same replica of above service but with POST signature when i call that service it calls service without any problem
Post service code is
#POST
#Path("/getresponses/{caller}")
#Produces({MediaType.APPLICATION_JSON})
public Response getResponseData1(#PathParam("caller") String caller ,#QueryParam("ticket")String ticket ,#FormParam("formParam") String data){
ResponseBuilder resp;
System.out.println("name of caller is -> "+ caller);
System.out.println("query param ticket -> "+ ticket);
System.out.println("form param data->" + data);
Employee emp = new Employee();
emp.setAge(23);
emp.setName("data");
Gson gson = new Gson();
String responseJson = gson.toJson(emp);
resp=Response.ok(responseJson);//header("Access-Control-Allow-Origin", "*")
resp.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST");
return resp.build();
}
My client side code is
$(document).ready(function(){
// for post service
$('#sendcall').on('click',function(e){
var dataTosend ="formParam=data to send";
$.ajax({
url: 'http://someip:8099/Jqgrid/rest/getdata/getresponses/data?ticket=tick',
contentType : 'application/x-www-form-urlencoded',
data :dataTosend,
type: 'POST',
success: function(data){
alert(data);
}
});
});
//for PUT service
$('#sendcall2').on('click',function(e){
var datatosend ="formParam=data to send";
$.ajax({
url: 'http://someip:8099/Jqgrid/rest/getdata/getresponse/aliahsan?ticket=tick',
contentType : 'application/x-www-form-urlencoded',
data :datatosend,
type: 'PUT',
crossDomain:true,
beforeSend: function (xhr) {
console.log('header added');
},
success: function(data){
alert(data);
}
});
});
});
Please help me in this regard why PUT is not working with this.
Any help will be greatly appreciated
Instead of adding all the CORS headers inside your resource method, use a Jersey filter, as described in this post. The reason for this, is the CORS preflight request, which is defined in HTTP access control (CORS) as:
"preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send.
So the request is an OPTIONS request and it expects back the the "Accept-Xxx" CORS headers to determine what is allowed by the server. So putting the headers in the resource method has no affect as the the request is made with the OPTIONS HTTP method, which you don't have a resource method for. This generally leads to a 405 Method Not Allowed error sent to the client.
When you add the headers in the filter, every request goes through this filter, even the OPTIONS request, so the preflight gets the according headers.
As for the PUT, also described in the above linked document (continuing from the above quote)
Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
This is why the POST request doesn't face the same problem.

Resources