Angular 2 REST request HTTP status code 401 changes to 0 - rest

I'm developing an Angular2 application. It seems when my access-token expires the 401 HTTP Status code gets changed to a value of 0 in the Response object. I'm receiving 401 Unauthorized yet the ERROR Response object has a status of 0. This is preventing me from trapping a 401 error and attempting to refresh the token. What's causing the 401 HTTP status code to be changed into HTTP status code of 0?
Here's screenshot from Firefox's console:
Here's my code:
get(url: string, options?: RequestOptionsArgs): Observable<any>
{
//console.log('GET REQUEST...', url);
return super.get(url, options)
.catch((err: Response): any =>
{
console.log('************* ERROR Response', err);
if (err.status === 400 || err.status === 422)
{
return Observable.throw(err);
}
//NOT AUTHENTICATED
else if (err.status === 401)
{
this.authConfig.DeleteToken();
return Observable.throw(err);
}
else
{
// this.errorService.notifyError(err);
// return Observable.empty();
return Observable.throw(err);
}
})
// .retryWhen(error => error.delay(500))
// .timeout(2000, new Error('delay exceeded'))
.finally(() =>
{
//console.log('After the request...');
});
}
This code resides in a custom http service that extends Angular2's HTTP so I can intercept errors in a single location.
In Google Chrome, I get this error message:
XMLHttpRequest cannot load https://api.cloudcms.com/repositories/XXXXXXXXXXXXXXXX/branches/XXXXXXXXXXXXXXXX/nodesXXXXXXXXXXXXXXXX. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://screwtopmedia.local.solutiaconsulting.com' is therefore not allowed access. The response had HTTP status code 401.
This is confusing because I am including 'Access-Control-Allow-Origin' header in request.
Here's a picture of results received in Google Chrome:
I've tried accessing 'WWW-Authenticate' Response Header as a means to trap for 401. However, the following code returns a NULL:
err.headers.get("WWW-Authenticate")
It's puzzling that I'm getting a CORS issue because I'm not getting any CORS errors when a valid access token is provided.
How do I trap for 401 HTTP status code? Why is 401 HTTP status code being changed to 0?
Thanks for your help.

The issue is related to CORS requests, see this github issue
No 'Access-Control-Allow-Origin' header is present on the requested
resource
means that 'Access-Control-Allow-Origin' is required in the response headers.
Angular is not getting any status codes, that's why it gives you a 0 which is caused by browser not allowing the xml parser to parse the response due to invalid headers.
You need to append correct CORS headers to your error response as well as success.

If cloudcms.com do to not set the Access-Control-Allow-Origin in 401 response, then nothing much you can do. You properly have to open support ticket with them to confirm if that is normal behavior.
javascript in browsers(FF, Chrome & Safari) I tested won't receive any info if CORS error occur, other than status 0. Angular2 has no control of it.
I created a simple test and get the same result as yours:
geturl() {
console.log('geturl() clicked');
let headers = new Headers({ 'Content-Type': 'application/xhtml+xml' });
let options = new RequestOptions({ 'headers': headers });
this.http.get(this.url)
.catch((error): any => {
console.log('************* ERROR Response', error);
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Web Server error';
return Observable.throw(error);
})
.subscribe(
(i: Response) => { console.log(i); },
(e: any) => { console.log(e); }
);
}
After much googling, I found the following(https://github.com/angular/angular.js/issues/3336):
lucassp commented on Aug 19, 2013
I found the issue for a while now but I forgot post here a reply. EVERY response, even Error 500, must have the CORS headers attached. If the server doesn't attach the CORS headers to the Error 500 response then the XHR Object won't parse it, thus the XHR Object won't have any response body, status, or any other response data inside.
Result from Firefox network monitor seems supporting the above reason.
Javascript request will receive empty response.
Plain url request(copy&paste the link in the address bar) will get response body
Javascript use XHRHttpRequest for http communication.
When a XHR reply arrive, the browser will process the header, that's why you will see those 401 network messages. However, if the XHR reply is from a different host then the javascript AND the response header contain no CORS header(eg Access-Control-Allow-Origin: *), the browser will not pass any info back to the XHR layer. As a result, the reply body will be completely empty with no status(0).
I tested with FF 48.0.1, Chrome 52.0.2743.116 and Safari 9.1.2, and they all have the same behavior.
Use browser network monitor to check response header for those 401 Unauthorized entries, it is very likely that there is no Access-Control-Allow-Origin header and causing the issue you are facing. This can be a bug or by design on the service provider side.
Access-Control-Allow-Origin is a response only http header. For more information of https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_response_headers

I work for Cloud CMS. I can confirm that we properly set CORS headers on API calls. However, for 401 responses the headers are not getting set properly. This has been resolved and the fix will be available on the public API at the end of this week.
BTW if you use our javascript driver https://github.com/gitana/gitana-javascript-driver instead of writing to the API directly then the re-auth flow is handled automatically and you do not need to trap 401 errors at all.

Stumbled upon this item:
What happens in my case was that:
I was requesting REST from different domain
The resource returned 401, but there was no "Access-Control-Allow-Origin" set in the error response.
Chrome (or your browser) received 401 and showed in network tab (See below count : 401), but never passed to Angular
since there was no CORS allowed (missing "Access-Control-Allow-Origin" header) - Chrome could not pass this response into Angular. This way I could see 401 in the Chrome but not in Angular.
Solution was simple (extends on TimothyBrake's answer) - to add the missing header into the error response. In Spring boot I put: response.addHeader("Access-Control-Allow-Origin", "*"); and I was sorted out.
#Component
public class JwtUnauthorizedResponseAuthenticationEntryPoint
implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"You would need to provide the Jwt Token to Access This resource");
}
}
PS: Make sure the bean is provided into HttpSecurity config in your WebSecurityConfigurerAdapter. Refer to: https://www.baeldung.com/spring-security-basic-authentication if in doubt.
Hope this helps.

According to W3C, if the status attribute is set to 0, it means that:
The state is UNSENT or OPENED.
or
The error flag is set.
I think that this isn't an Angular2 issue, it seems like your request has a problem.

I had the same issue and was due to the server not sending the correct response (even though the console log stated a 401 the Angular error had status 0). My server was Tomcat with Java application using Spring MVC and Spring Security.
It is now working using folowing setup:
SecurityConfig.java
...
protected void configure(HttpSecurity http) throws Exception {
....
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
// allow CORS option calls
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
...
SomeAuthenticationEntryPoint.java
#Component
public class SomeAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
HttpStatus responseStatus = HttpStatus.UNAUTHORIZED;
response.sendError(responseStatus.value(), responseStatus.getReasonPhrase());
}
}
web.xml
<error-page>
<error-code>403</error-code>
<location>/errors/unauthorised</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errors/unauthorised</location>
</error-page>
Controller to handle the /errors/... previously defined
#RestController
#RequestMapping("/errors")
public class SecurityExceptionController {
#RequestMapping("forbidden")
public void resourceNotFound() throws Exception {
// Intentionally empty
}
#RequestMapping("unauthorised")
public void unAuthorised() throws Exception {
// Intentionally empty
}
}
}

You should use a 3rd party http protocol monitor (like CharlesProxy) rather than Chrome dev tools to confirm which headers are actually being sent to the API service and if it is returning a 401.

Related

Chrome DevTools Protocol Fetch Domain - getResponseBody - apparently fails with HTTP redirects

I am wishing to collect the body of an HTTP request, including when the page redirects to elsewhere. Clearly, I can use non-Fetch domain mechanisms such as Network.getResponseBody. That works fine for the "final" page in a chain of redirections, but cannot be used for the intermediate pages because Chrome appears to dump the content when going to the next redirection target.
So, I implemented Fetch.enable( { patterns: [ { requestStage: Response } ] } ) (using PHP, but the details of that are irrelevant, as you will see). No error is returned from this method call. After then doing a Page.navigate, I wait for a Fetch.requestPaused event which contains members requestId, responseStatusCode and responseHeaders and then send a Fetch.getResponseBody (using the requestId from the Fetch.requestPaused) and the response I get depends on what the actual response to the page itself was. So, for a 200, I get a response body (hurray), but for a 30x (301, 302 etc), I always get error code -32000 with the message "Can only get response body on requests captured after headers received". Now, issuing that error message is inconsistent (in my view) with the Fetch.requestPaused event data, even if Chrome DevTools Protocol (CDP) was not intended to capture the bodies of HTTP redirected pages. By the way, pages with content triggered redirection (via a META element or JavaScript) are captured okay, I assume because they return a 200 status code.
So, is the issue in the sequence of calls I'm following or in the error message returned by Fetch.getResponseBody and am I correctly assuming CDP was not intended to capture the bodies of documents in a redirection chain (apart from the last one, obviously)?
You need to continue the request on a 301/302 and let the browser follow it (there is no body in a redirect):
if (
params.responseStatusCode === 301 || params.responseStatusCode === 302
) {
await this.#client.send('Fetch.continueRequest', {
requestId,
});
} else {
// get body here
const responseCdp = await this.#client.send('Fetch.getResponseBody', {
requestId,
});
await this.#client.send('Fetch.fulfillRequest', {
requestId,
responseCode: params.responseStatusCode,
responseHeaders: params.responseHeaders,
body: responseCdp.body,
});
}

cant set Authorization Header in HttpClient ( .Net core 3.1 )

I am migrating from .net core 2.2 to 3.1. I am making an XUnit test method to test my controllers.
I successfully made and tested in .net core 2.2 projects, but after migrating to 3.1 it seems it cant set authorization header to my request so I am getting UnAuthorized from my app.
this is my Code :
[Fact]
public async void InvalidId_UnSuccessFull_GetById()
{
// Arrange
var httpClient = new HttpClient();
var token = await GetAdminAccessToken(); // Sends a login request and fetch a valid token
// httpClient.DefaultRequestHeaders.Add("Authorization",$"Bearer {token}");
httpClient.DefaultRequestHeaders.Authorization=new AuthenticationHeaderValue("Authorization",$"Bearer {token}");
var id = Guid.Empty;
// Act
var response = await httpClient.GetAsync("localhost:5000/Admin/User/{id}");
var message = await ExtractMessage(response);
// Assert
Assert.Contains(PersianErrorMessage.InvalidUserId, message);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
I debugged into httpClient class till the sendAsync method and the HttpRequestMessage request instance does not have an Authorization Header that I set above! What's wrong with my code?
The AuthenticationHeaderValue should be set like this:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Source: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.headers.authenticationheadervalue.-ctor?view=netcore-3.1#System_Net_Http_Headers_AuthenticationHeaderValue__ctor_System_String_
There are a couple of issues that could trip you up here. This can be a frustrating issue, so hopefully, this helps. The HTTP client is more than likely sending the Authorization header. If there is any kind of redirect, the Authorization header does not travel with it. I ran into this issue once and the only problem was my original URL did not have a trailing slash at the end.
What I entered:
https://api.example.com/v3/endpoint
Server endpoint:
https://api.example.com/v3/endpoint/
Step 1, Determine if there is a redirect.
Step 2, Make your URL match the final URI exactly
Clients like curl do this automatically. In .Net 3.1, you will have to make sure your client checks for redirects. A 401 error is actually a good thing--it means you more than likely reached the right endpoint albeit without the authorization token.
You can do this in code, but just run debug and break after you get the response. Examine the RequestURI property of the RequestMessage object of the HttpResponseMessage (response.RequestMessage.RequestURI). Compare this with your initial URL. If they do not match exactly, you've been redirected and the Authorization header was lost along the way.

How to create meaningful REST response messages?

I have created a REST service and I was wondering what the best practice was for sending meaningful messages to a GET request. Basically my GET request returns a specific object, something like this;
#GET
#Path("/examsple")
#Produces(MediaType.APPLICATION_JSON)
public List<SomeObject> retrieveSomeObjs() {
List<SomeObject> result = new ArrayList<>();
try {
result = ... Get SomeObjects ...;
} catch (Exception e) {
... Deal with exception ...
}
return result;
}
That works great except when there is an error the response just sends back an empty List! What would be more useful would be a message that explains what the problem is. However I cant send back a String message because the return type is List!
My current solution is to change the return type to a Map and then I can return the object wrapped in the Map along with any messages. However its a little messy on the client side and I was wondering if there was either an inbuilt solution or an 'accepted' solution for this.
If the client has made an error then use HTTP Response codes. If an item is not found then your response would be a 404 Not Found. If the user does not have permissions to access an object then return a 403 Forbidden. Currently you are responding with a 200 OK saying everything is OK when it's not.
If it's an error at the server side you don't really want to be sending that information to your clients. Catch the error on the server and do something meaningful with it (like log it) so you can change the code so it doesn't happen again.
You could return an HTTP error status code in the header and a JSON response body with an object describing the exception.
As already mentioned some common error codes for GET requests include:
301 Moved Permanently - If the resource has been moved
400 Bad Request - If the client request is unaccaptable, i.e. if the client sends none-sense parameters in the request
401 Unauthorized - If the client did not provide any valid credentials
403 Forbidden - If the client is authorized but not allowed to perform the request (you can also return a 404 in this case to conceal that this resource exists at all)
404 Not Found - If the requested resource could not be found
I usually create a POJO to represent these error messages and then return it using a Jersey Response object.
For example the error object could look like this:
public class ApiError {
private String status;
private String code;
private String message;
private String developerMessage;
// Getters and Setters here
}
To return it you can do the following (i.e. in your catch block or your custom ExceptionMapper):
ApiError error = new ApiError("409", "409-1", message, developerMessage);
return Response.status(Response.Status.CONFLICT).entity(error).build();
This way you can provide nicely formatted JSON/XML error messages containing custom error codes and further information for the developer. The error entities will get serialized according to your #Produces annotation.

GWT JSONPRequestBuilder - "Unknown Token :" error in browser console with Timeout exception at the end

I am trying to call a Restful WS from GWT using JSOPRequestBuilder. I have a similar issue which was reported in the link
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/ef93383aca7a3dfc/d4dc5bad1a9110ea
But, I could not figure out the solution. Kindly help me at the earliest.
My JAX-WS resource code snippet from server
#GET
#Produces(MediaType.APPLICATION_JSON)
public DealerAddress getDealerAddress(#QueryParam("dealerId") String sDealerId) {
DealerAddress dlrAd = new DealerAddress("test", "test", "test", "test", 10, new Date(), new Date());
return dlrAd;
}
Jersey returns a JSON object of DealerAddress.
Now rest URL "https://127.0.0.1:8181/application/rest/OrderManagementResource?alt=json-in-script&dealerId=DLR1"
works absolutely fine when i tried request in browser.
It even works with RequestBuilder approach from GWT but not with JSONPRequestBuilder approach.
Code snippet to invoke WS from GWT using JSONPRequestBuilder
JsonpRequestBuilder jsonPReqBuilder = new JsonpRequestBuilder();
jsonPReqBuilder.setTimeout(100000);
jsonPReqBuilder.setCallbackParam("callback");
jsonPReqBuilder.requestObject("https://127.0.0.1:8181/application/rest/OrderManagementResource?alt=json-in-script&dealerId=DLR1" , new AsyncCallback<DealerAddressJSON>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
caught.printStackTrace();
Window.alert("Inside error"+caught.getLocalizedMessage());
}
#Override
public void onSuccess(DealerAddressJSON result) {
// TODO Auto-generated method stub
Window.alert("Inside success"+result);
}
});
where as DealerAddressJSON is a JavaScriptObject type class.
I could see that my JAX Rest resource getting called and saying returning from server.
Also, I could see that in Firebug that the response comes in browser but fails with an exception "Unknown token :"
At the end I always get a Timeout exception.
Now I am in big question whether the way we return JSON from JAX-RS resource is a problem in server
or
JSONPRequestBuilder calling procedure is a problem? I could not understand the callback changes which some of the links explained on this issue.
Kindly help me.
You are probably sending back JSON, while the JSONPRequestBuilder expects JSONP. These are not the same thing.
JSON is just the data, as is - make the request using AJAX (i.e. the RequestBuilder), and the contents can be read directly. These requests can only be made to the same server. Example JSON data:
{"response":"success", "items":[{"id":1}, {"id":2}]}
In contrast, JSONP is designed for cross-origin requests, so instead of just containing the data, the data is wrapped up in a JavaScript. Since your JSON service isn't wrapping the a response in a js function call, this isn't working. Example JSONP data:
callback_1({"response":"success", "items":[{"id":1}, {"id":2}]})
The callback changes with each request, so the server is supposed to change that callback function based on what the client requested each time.

How to fix Jersey POST request parameters warning?

I'm building a very simple REST API using Jersey, and I've got a warning in my log files that I'm not sure about.
WARNING: A servlet POST request, to
the URI
http://myserver/mycontext/myapi/users/12345?action=delete,
contains form parameters in the
request body but the request body has
been consumed by the servlet or a
servlet filter accessing the request
parameters. Only resource methods
using #FormParam will work as
expected. Resource methods consuming
the request body by other means will
not work as expected.
My webapp only has the Jersey servlet defined, mapped to /myapi/*
How can I stop these warnings?
For me the warning was showing for POST application/x-www-form-urlencoded. And I am using Spring Boot which has an HiddenHttpMethodFilter that does a getParameter before anything else... So I ended up doing this nasty override:
#Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if ("POST".equals(request.getMethod())
&& request.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
};
}
This message is meant to warn developers about the fact that the request entity body has been consumed, thus any other attempts to read the message body will fail.
It is safe to ignore the message or filter it out from the logs:
java.util.logging.Logger jerseyLogger =
java.util.logging.Logger.getLogger(WebComponent.class.getName());
jerseyLogger.setFilter(new Filter() {
#Override
public boolean isLoggable(LogRecord record) {
boolean isLoggable = true;
if (record.getMessage().contains("Only resource methods using #FormParam")) {
isLoggable = false;
}
return isLoggable;
}
});
The following thread describes the warning you are receiving. It sounds as though you might have a filter defined in your web.xml that is processing the request before Jersey does.
Finally got rid of this by making sure I had Content-Type: application/json in my request headers (obviously, on the client side)
I just had my ajax-function in JQuery set to contentType: "application/x-www-form-urlencoded; charset=UTF-8" because with a prior solution (without Jersey) I had some encoding problems. When I removed that the message was gone and everything worked fine.
This warning is the only thing the WebComponent logs, so just turn logging up to ERROR level or turn off logging for this component in your logback.xml or wherever you have logging configured. You don't need to write a custom filter to ignore this specific message since there are no other messages logged from this component.
Source code snippet from org.glassfish.jersey.servlet.WebComponent version 2.14:
if(!form.asMap().isEmpty()) {
containerRequest.setProperty("jersey.config.server.representation.decoded.form", form);
if(LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LocalizationMessages.FORM_PARAM_CONSUMED(containerRequest.getRequestUri()));
}
}
The localized message that is used for this warning message is:
form.param.consumed=A servlet request to the URI {0} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
Turn logging off for the WebComponent in your logback.xml like so:
<logger name="org.glassfish.jersey.servlet.WebComponent" level="OFF" additivity="false"/>
Right.
So I've been suffering this issue, and I've been trying to solve it on different ways, but I did't want to change my web.xml settings, just because if I was testing my application with Postman it worked perfect, but when it was being integrated with the webapp it fails with the mentioned issue (A servlet request to the URI {MY_URI} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.)
So as #clijk mentioned, you only have to set your headers as:
"Content-Type":"application/json"
"charset":"UTF-8"
and voilá, the warning it's gone.
Thanks
In my case I've fixed this error when I've changed the Object Date to String in the method.
Error:
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") Date date) throws Exception {
Fixed
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") String date) throws Exception {
Put this to your resource signature. Or find this string in your project someone already use this if #PUT or #POST is used. This should help
import javax.ws.rs.Consumes;
#Consumes(MediaType.APPLICATION_JSON)