I have an interceptor using $httpProvider as follwos (taken from Angular doc)
$httpProvider.interceptors.push(function ($q) {
return {
'response': function (response) {
// look for the antiforgerytoken and update dataService with the same.
return response || $q.when(response);
}
};
});
using the debugger I see that this method is called on each response and when I look at the 'response' object pased I see only one header, namely the Accept-Header as shown below
When I look at the response packet in the chrome I see many more headers
My question is why am I not able to see all the headers in the response?
Specifically I want to read the cookie for AntiforgeryToken and store it, how can I do that?
I believe the cookie AntiforgeryToken is a HttpOnly cookie and cannot be accessed from javascript. Can you check
See some details here http://blog.codinghorror.com/protecting-your-cookies-httponly/
Such cookies are mean for server exclusively.
Related
I have a Jetty http server with some Jersey rest services. Those services are called from a React website that runs on a Node server.
Due to the cross origin nature of this setup, I had to add some HTTP headers. Basically, all my webservices return a createOkResult() which is created as follows.
#POST
#Path("orders/quickfilter")
#Consumes(MediaType.APPLICATION_JSON)
public Response getQuickFilterProductionOrders(String data)
{
...
return createOkResult(json.toString());
}
protected Response createOkResult(Object result)
{
return buildCrossOrigin(Response.ok().entity(result));
}
protected static Response buildCrossOrigin(Response.ResponseBuilder responseBuilder)
{
return responseBuilder.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
For the #GET webservices that works fine. But when I create an #POST service, I just can't get it working.
Webbrowsers (chrome and firefox) return these kind of errors:
Access to XMLHttpRequest at 'http://localhost:59187/rs/production/orders/quickfilter' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
So, at first sight I would be tempted to think that the headers are still missing. The thing is, when I test this service with a tool like Postman, then all headers turn out to be in place, and the service even returns the requested data.
This is a screenshot of a POST request.
From my front-end (which runs on the node server), I use the axios API, which uses promises, and my request looks like this:
const url = "http://localhost:59187/rs/production/orders/quickfilter";
const data = JSON.stringify(request);
const headers = { headers: { "Content-Type": "application/json" } };
const promise = axios.post(url, data, headers);
Right now I have a HTTP error 500, If I remove the content type header, I get an unsupported media exception. So, I have reasons to believe that the content type is ok.
Paul Samsotha pointed me in the right direction.
I ended up adding a filter to the ServletContextHandler. Unlike the linked article, I didn't really have to create that filter from scratch. There was an existing filter class that I could use: i.e. org.eclipse.jetty.servlets.CrossOriginFilter.
FilterHolder filterHolder = context.addFilter(CrossOriginFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filterHolder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
filterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
Some of the above parameters can probably be left out, as they are default values. But what appeared to be crucial for me, was to set the CHAIN_PREFLIGHT_PARAM to false.
One nice side-effect, is that I can simplify the code of the actual services. They do not longer need to add special headers, by contrast they can now just return Response.ok().entity(result).build();.
I have a main REST web app where I have an endpoint: POST /api/v1/my_endpoint
I want to allow it to be called:
1) via a browser via ajax from my other web apps on different domains
2) from a server side via HTTP client library.
In the case of ajax call - #1 - I'll have to include "Allow-Control-Allow-Origin" and the similar headers to my response to let a browser receive a response.
In the case #2 - I won't need to include those headers.
However, there's no reliable way to distinguish between #1 and #2.
How can I solve that?
On the server, you check for the presence of an Origin header. This header is sent by the browser as part of the CORS protocol. To explain how it works, below is a filter (in other frameworks, this is also known as middleware) used by a Jersey application. Jersey is a Java REST framework. Sorry I don't know Rails. But you should still be able to follow along with this explanation,.
How this filter works is that the ContiainerRequestFilter is called before the backend controller method is called, then the controller method is called, then the ContainerResponseFilter is called. See the commented notes above the methods to see which method are for which.
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Override
// ******************************************
// implementation for ContainerRequestFilter
// ******************************************
public void filter(ContainerRequestContext request) throws IOException {
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
#Override
// ******************************************
// implementation for ContainerResponseFilter
// ******************************************
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
if (request.getHeaderString("Origin") == null) {
return;
}
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, " +
"Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
}
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
So when the request comes in, the filter checks to see if it is Preflight request. There are two types of cross origin request: a normal one, and preflight. The preflight request occurs before the actual request is made to the server. This is an OPTIONS request where the browser sends some CORS request headers to check with the server to see if the request is allowed. The server should respond back with CORS response headers telling the server that the request is allowed.
So with this filter, is it a preflight request, we abort the request (the controller method will not be called) and then the code execution goes to the ContainerResponseFilter where we set the CORS response headers. You can see in the code, we check the same isPreflightRequest() method.
If the request is not an OPTIONS request, and the Origin header is present, then it is a "normal" cross origin request, in which case, all that is required in in the response headers is Access-Control-Allow-Origin.
Notice that if there is no Origin header, then no CORS headers are added. This is basically how you will differentiate from an AJAX client and other HTTP clients. Only AJAX requests will add the Origin header, and this is done automatically by the browser when it detects a cross origin request.
I hope you understand everything I am talking about, even though the code is Java. It think it's pretty straight forward though, even if you have never used Java. The naming or methods and variables should make it easy to follow. If you understand the flow of the code, then you should pretty much understand the flow of the CORS protocol.
Aside
As an aside, it doesn't hurt if you add the Access-Control-Allow-Origin header on all responses, whether it be an AJAX request or an HTTP client request. If you want to be lazy and implement it this way, it won't hurt. It is required for the AJAX client, but not for the HTTP client. But if you include it in the HTTP client, nobody will die. The world will go on business as usual.
When i POST a request to my .net core 2 mvc backend it returns json data.
I want to optionally change the headers as so , which i will then return a csv file of the data for download
'Accept': 'text/csv',
'Content-Type': `text/csv; charset=utf-8`
I set the controller base class with this Produces filter
[Produces("application/json", "text/csv")]
But those headers always cause .net to return 415 Unsupported Media Type
The controller action looks like this
[HttpPost]
public async Task<IActionResult> Post([FromBody] PostArgs args)
You source of problem is Content-Type: text/csv; charset=utf-8 header.
[FromBody] forces MVC middleware to use the input formatter for model binding (I am talking about PostArgs model). And by default, ASP.NET Core registers only one, JSON input formatter. Cause you set Content-Type, middleware cannot use that default formatter (as Content-Type header says that data in request body should be processed as CSV, not JSON) and so it throws 415 Unsupported Media Type error.
... I want to optionally change the headers as so , which i will then return a csv file of the data for download
Actually, it looks like you understand in wrong way what Content-Type header does:
In requests, (such as POST or PUT), the client tells the server what type of data is actually sent.
In other words, you only need to specify the Accept header, cause
The Accept request HTTP header advertises which content types, expressed as MIME types, the client is able to understand. Using content negotiation, the server then selects one of the proposals.
And it is the server then, who uses a Content-Type header in responses to tell the client what the content type of the returned content (in response) actually is.
To return csv data, you return a ContentResult rather than a JsonResult object. This allows you to define the Content-Type:
return new ContentResult("csv-data", "text/csv", 200);
If you want to return a physical file you could return a FileResult object.
By default, the Accepts header isn't enforced. You can enforce it via configuration:
services.AddMvc(config =>
{
config.RespectBrowserAcceptHeader = true;
});
In order to accept additional formats, you'll also need to add InputFormatters:
services.AddMvc(config =>
{
config.RespectBrowserAcceptHeader = true;
config.InputFormatters.Add(new TextInputFormatter())
config.OutputFormatters.Add(new StringOutputFormatter());
});
Considering that in my Zend Framework module I'm able to access via browser or REST. When I got a 404 via REST, this is the content:
{"httpStatus":404,"title":"Not Found"}
So how do I test this response using PHPUnit?
I have already a test for another route that is working:
public function testIndexActionCanBeAccessed()
{
$this->dispatch('/ws/dashboard');
$this->assertResponseStatusCode(200);
$this->assertModuleName('Ws');
$this->assertControllerName('Ws\Controller\Dashboard');
$this->assertControllerClass('DashboardController');
$this->assertMatchedRouteName('application/ws');
}
But I don't know how to do this other case.
public function testIndexActionCanBeAccessed()
{
$this->dispatch('/ws/asd');
$this->assertResponseStatusCode(404); // this is OK
// but how check the json response?
}
Also, if I do this $response = $this->getResponse(); I got the entire HTML 404 html file, which is not what I want.
You may want to check to ensure that the controller is actually returning a JSON response.
Also, your Accept header for the AJAX request should specify application/json.
I'm trying to send an XMLHttpRequest object to my Rails server but the headers are causing my function to stop. Here are my observations:
When I comment out the 3 lines of code that set the headers, then xhr.readyState will eventually equal 4 (alert boxes within the anonymous function fire off).
If any one of the 3 header lines are uncommented, then the xhr object never changes state (none of the alert boxes ever fire off).
function saveUserProfile(){
var user_email = $('#userEmail_box').val();
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState==4 && xhr.status==200)
{
alert("Yes: " + xhr.readyState);
}
alert("No: " + xhr.readyState);
}
var method = 'POST';
var params = 'userEmail=user_email';
var url = 'http://localhost:3000/xhr_requests.json';
var async = true;
//Need to send proper header information with POST request
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.open(method, url, async);
xhr.send(params);
}
My three questions are:
What do I need to change in the code above in order to send data through the POST method?
I'm under the impression that the POST method requires some headers to be sent but am not clear about which ones though "xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');" seems to be one that is often mentioned in references. Can somebody help me understand a) why headers need to be sent and b) which ones need to be sent?
I'm using a rails server and am developing locally. Ultimately, this code will be executed on the client side of a mobile device which will go to a hosted rails server for passing and receiving data. Are there limitations with using the POST method with a rails server? Keep in mind that I plan to use JSON when sending information to the client from the server.
Thanks!
UPDATE: The headers should come AFTER the opening the xhr request but BEFORE sending it:
xhr.open(method, url, async);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.send(params);
Hope this post saves somebody else 4 hours.
Does your web page with the JavaScript code also live on localhost:3000? If not, this is considered a cross-domain request, and your server will need to return special headers. So you have two options:
1) Host the web page on the same domain as the server, which will make this a same-domain request.
2) Have your server return the appropriate CORS headers. You can learn more about CORS here: http://www.html5rocks.com/en/tutorials/cors/