Keywords: AngularJS | CORS | Preflight Request
Abstract: This article explores the issue of OPTIONS preflight requests in AngularJS applications when handling Cross-Origin Resource Sharing (CORS). Through a detailed case study, it explains the triggers for preflight requests, particularly the impact of Content-Type header settings. Based on best practices, it provides solutions to avoid preflight by adjusting Content-Type to text/plain or application/x-www-form-urlencoded, and discusses other headers that may trigger preflight. The article also covers the fundamentals of CORS and browser security policies, offering comprehensive technical guidance for developers.
Problem Background and Case Analysis
In mobile app development, cross-origin requests are common, but browser security policies like Cross-Origin Resource Sharing (CORS) can introduce additional complexity. A typical scenario involves converting a PhoneGap app to a mobile website, where previously functional POST requests fail in the browser environment. Specifically, when using AngularJS 1.2.0 to send a POST request to a third-party API, the browser first sends an OPTIONS preflight request, disrupting the request flow. The user's code example is as follows:
var request = {
language: 'fr',
barcodes: [
{
barcode: 'somebarcode',
description: 'Description goes here'
}
]
};
var config = {
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
}
};
$http.post('http://somedomain.be/trackinginfo', request, config).success(function(data, status) {
callback(undefined, data);
}).error(function(data, status) {
var err = new Error('Error message');
err.status = status;
callback(err);
});
Although the API server has added the domain to the Access-Control-Allow-Origin header, the preflight request still causes issues, as the developer cannot modify server-side configurations.
Mechanism of CORS Preflight Requests
CORS preflight requests are OPTIONS requests automatically sent by the browser before certain cross-origin requests to check if the server permits the actual request. According to Mozilla developer documentation, preflight requests are triggered by specific conditions, primarily non-simple request headers or methods. In the user's case, the key trigger is the Content-Type set to application/json. Browsers consider such Content-Type as a non-simple header, thus initiating preflight. Other headers that may trigger preflight include Authorization, Cache-Control, and other custom headers.
Solution: Adjusting Content-Type to Avoid Preflight
Based on the best answer, the most direct solution is to modify the Content-Type to comply with simple request standards. Simple request Content-Type values include text/plain, application/x-www-form-urlencoded, and multipart/form-data. Changing the Content-Type in the user's code from application/json to text/plain can avoid preflight requests, as text/plain is considered a simple header. Example modification:
var config = {
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'text/plain'
}
};
However, note that if the API server expects JSON-formatted data, this change may cause server-side parsing errors. Therefore, developers should ensure the request payload format matches the Content-Type. For instance, when using application/x-www-form-urlencoded, the JSON object must be converted to a URL-encoded string.
Other Potential Triggers and Handling Strategies
If preflight requests persist after changing Content-Type, possible reasons include X-headers automatically added by AngularJS or other custom headers. Developers should inspect request configurations to remove or simplify unnecessary headers. Additionally, ensure the request method is a simple method (e.g., GET, POST, HEAD), as methods like PUT or DELETE may also trigger preflight. In practice, it is recommended to use browser developer tools to monitor network requests and identify specific triggers.
Technical Implementation Details and Code Examples
To illustrate in depth, here is a complete example demonstrating how to adjust requests to avoid preflight. Assume the API server accepts application/x-www-form-urlencoded format:
// Convert JSON object to URL-encoded string
function toUrlEncoded(obj) {
return Object.keys(obj).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])).join('&');
}
var requestData = toUrlEncoded({
language: 'fr',
barcodes: JSON.stringify([{ barcode: 'somebarcode', description: 'Description goes here' }])
});
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
$http.post('http://somedomain.be/trackinginfo', requestData, config).then(function(response) {
callback(undefined, response.data);
}, function(error) {
var err = new Error('Error message');
err.status = error.status;
callback(err);
});
This approach ensures the request complies with simple request standards by formatting the payload and adjusting Content-Type, thereby skipping preflight.
Conclusion and Best Practices
When handling CORS preflight requests, the core lies in understanding browser security policies and the definition of simple requests. Developers should prioritize adjusting client-side code, such as modifying Content-Type or simplifying headers, rather than relying on server-side changes. In AngularJS, careful configuration of the $http service can effectively avoid preflight issues. Additionally, consider using proxy servers or JSONP as alternatives, but note their limitations. Through this analysis, developers can better address cross-origin request challenges, improving application compatibility and performance.