cross-domain - headers - falta la cabecera cors ''access-control-allow-origin'' angular
AngularJS realiza una solicitud HTTP de OPCIONES para un recurso de origen cruzado (13)
Estoy tratando de configurar AngularJS para que se comunique con un recurso de origen cruzado donde el host del activo que entrega mis archivos de plantilla se encuentra en un dominio diferente y, por lo tanto, la solicitud de XHR de que las actuaciones angulares deben ser de dominio cruzado. He agregado el encabezado CORS apropiado a mi servidor para que la solicitud HTTP haga que esto funcione, pero no parece funcionar. El problema es que cuando inspecciono las solicitudes HTTP en mi navegador (chrome), la solicitud enviada al archivo de activos es una solicitud de OPCIONES (debe ser una solicitud GET).
No estoy seguro de si se trata de un error en AngularJS o si necesito configurar algo. Por lo que entiendo, el contenedor XHR no puede realizar una solicitud de OPCIONES HTTP, por lo que parece que el navegador está tratando de averiguar si está "permitido" descargar el activo antes de realizar la solicitud GET. Si este es el caso, entonces ¿necesito configurar el encabezado CORS (Access-Control-Allow-Origin: http://asset.host .... ) También con el host activo?
De alguna manera lo arreglé cambiando
<add name="Access-Control-Allow-Headers"
value="Origin, X-Requested-With, Content-Type, Accept, Authorization"
/>
a
<add name="Access-Control-Allow-Headers"
value="Origin, Content-Type, Accept, Authorization"
/>
El mismo documento dice
A diferencia de las solicitudes simples (explicadas anteriormente), las solicitudes "preclighted" envían primero un encabezado de solicitud HTTP OPTIONS al recurso en el otro dominio, para determinar si la solicitud real es segura para enviar. Las solicitudes de sitios cruzados se verifican de esta manera, ya que pueden tener implicaciones para los datos del usuario. En particular, una solicitud se marca previamente si:
Utiliza métodos distintos de GET o POST. Además, si se usa POST para enviar datos de solicitud con un tipo de contenido distinto de application / x-www-form-urlencoded, multipart / form-data o text / plain, por ejemplo, si la solicitud POST envía una carga XML al servidor utilizando application / xml o text / xml, entonces la solicitud se verifica previamente.
Establece encabezados personalizados en la solicitud (por ejemplo, la solicitud utiliza un encabezado como X-PINGOTHER)
Cuando la solicitud original es Obtener sin encabezados personalizados, el navegador no debe realizar una solicitud de Opciones como lo hace ahora. El problema es que genera un encabezado X-Requested-With que fuerza la solicitud de Opciones. Consulte https://github.com/angular/angular.js/pull/1454 sobre cómo eliminar este encabezado
Esta es la forma en que solucioné este problema en ASP.NET
Primero, debe agregar el paquete nuget Microsoft.AspNet.WebApi.Cors
Luego modifique el archivo App_Start / WebApiConfig.cs
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.EnableCors(); ... } }
Agregue este atributo en su clase de controlador
[EnableCors(origins: "*", headers: "*", methods: "*")] public class MyController : ApiController { [AcceptVerbs("POST")] public IHttpActionResult Post([FromBody]YourDataType data) { ... return Ok(result); } }
Así pude enviar a json a la acción.
$http({ method: ''POST'', data: JSON.stringify(data), url: ''actionurl'', headers: { ''Content-Type'': ''application/json; charset=UTF-8'' } }).then(...)
Referencia: habilitación de solicitudes de origen cruzado en ASP.NET Web API 2
Esto solucionó mi problema:
$http.defaults.headers.post["Content-Type"] = "text/plain";
Las solicitudes de OPCIONES no son, de ninguna manera, un error de AngularJS, así es como el Intercambio de Recursos Cruzados en el Origen obliga a los navegadores a comportarse. Consulte este documento: https://developer.mozilla.org/en-US/docs/HTTP_access_control , donde en la sección "Descripción general" dice:
El estándar de Intercambio de recursos de origen cruzado funciona al agregar nuevos encabezados HTTP que permiten a los servidores describir el conjunto de orígenes que tienen permitido leer esa información mediante un navegador web. Además, para los métodos de solicitud HTTP que pueden causar efectos secundarios en los datos del usuario (en particular, para los métodos HTTP que no sean GET, o para el uso de POST con ciertos tipos MIME). La especificación exige que los exploradores "verifiquen" la solicitud, solicitando métodos admitidos desde el servidor con un encabezado de solicitud de OPCIONES HTTP y, luego, "aprobando" desde el servidor, enviando la solicitud real con el método de solicitud HTTP real. Los servidores también pueden notificar a los clientes si deben enviarse las "credenciales" (incluidas las cookies y los datos de autenticación HTTP) con las solicitudes.
Es muy difícil proporcionar una solución genérica que funcione para todos los servidores WWW, ya que la configuración variará según el servidor en sí y los verbos HTTP que pretende admitir. Le animo a que supere este excelente artículo ( http://www.html5rocks.com/en/tutorials/cors/ ) que tiene muchos más detalles sobre los encabezados exactos que debe enviar un servidor.
Me rendí tratando de solucionar este problema.
Mi IIS web.config tenía los " Access-Control-Allow-Methods
" relevantes, experimenté agregando ajustes de configuración a mi código Angular, pero después de grabar unas pocas horas intentando que Chrome llamara a un servicio web JSON de varios dominios, Me rendí miserablemente.
Al final, agregué una página web de manejador de ASP.Net tonta, obtuve eso para llamar a mi servicio web JSON y devolver los resultados. Estaba en marcha y funcionando en 2 minutos.
Aquí está el código que utilicé:
public class LoadJSONData : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string URL = "......";
using (var client = new HttpClient())
{
// New code:
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING");
HttpResponseMessage response = client.GetAsync(URL).Result;
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync().Result;
context.Response.Write("Success: " + content);
}
else
{
context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase);
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Y en mi controlador Angular ...
$http.get("/Handlers/LoadJSONData.ashx")
.success(function (data) {
....
});
Estoy seguro de que hay una forma más simple / más genérica de hacer esto, pero la vida es demasiado corta ...
¡Esto me funcionó y ahora puedo seguir con mi trabajo normal!
Para Angular 1.2.0rc1 + necesita agregar un resourceUrlWhitelist.
1.2: versión de lanzamiento, agregaron una función escapeForRegexp para que ya no tenga que escapar de las cadenas. Solo puedes agregar la url directamente
''http://sub*.assets.example.com/**''
Asegúrese de agregar ** para las subcarpetas. Aquí hay un jsbin de trabajo para 1.2: http://jsbin.com/olavok/145/edit
1.2.0rc: si aún está en una versión rc, Angular 1.2.0rc1 la solución parece:
.config([''$sceDelegateProvider'', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([''self'', /^https?:////(cdn/.)?yourdomain.com/]);
}])
Aquí hay un ejemplo de jsbin donde funciona para 1.2.0rc1: http://jsbin.com/olavok/144/edit
Pre 1.2: para versiones anteriores (ref http://better-inter.net/enabling-cors-in-angular-js/ ) necesita agregar las siguientes 2 líneas a su configuración:
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common[''X-Requested-With''];
Aquí hay un ejemplo de jsbin en el que funciona para versiones pre 1.2: http://jsbin.com/olavok/11/edit
Para un proyecto IIS MVC 5 / Angular CLI (Sí, soy consciente de que su problema es con Angular JS) con API hice lo siguiente:
web.config bajo el nodo <system.webServer>
<staticContent>
<remove fileExtension=".woff2" />
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
</staticContent>
<httpProtocol>
<customHeaders>
<clear />
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
</customHeaders>
</httpProtocol>
global.asax.cs
protected void Application_BeginRequest() {
if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
Response.Flush();
Response.End();
}
}
Eso debería solucionar sus problemas tanto para MVC como para WebAPI sin tener que hacer todo el resto. Luego creé un HttpInterceptor en el proyecto Angular CLI que se agregó automáticamente en la información del encabezado relevante. Espero que esto ayude a alguien en una situación similar.
Perfectamente descrito en el comentario de pkozlowski. Tenía una solución de trabajo con AngularJS 1.2.6 y ASP.NET Web Api pero cuando actualicé AngularJS a 1.3.3, las solicitudes fallaron.
La solución para el servidor Web Api fue agregar el manejo de las solicitudes de OPCIONES al comienzo del método de configuración (más información en esta publicación del blog ):
app.Use(async (context, next) => { IOwinRequest req = context.Request; IOwinResponse res = context.Response; if (req.Path.StartsWithSegments(new PathString("/Token"))) { var origin = req.Headers.Get("Origin"); if (!string.IsNullOrEmpty(origin)) { res.Headers.Set("Access-Control-Allow-Origin", origin); } if (req.Method == "OPTIONS") { res.StatusCode = 200; res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST"); res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type"); return; } } await next(); });
Si está utilizando Jersey para API REST, puede hacer lo siguiente:
Usted no tiene que cambiar su implementación de servicios web.
Te lo explicaré para jersey 2.x
1) Primero agregue un ResponseFilter como se muestra abajo
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
public class CorsResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
}
}
2) luego en el web.xml, en la declaración de servlet de jersey, agregue lo siguiente
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
</init-param>
Si está utilizando un servidor nodeJS, puede usar esta biblioteca, funcionó bien para mí https://github.com/expressjs/cors
var express = require(''express'')
, cors = require(''cors'')
, app = express();
app.use(cors());
y después puedes hacer una npm update
.
Su servicio debe responder a una solicitud de OPTIONS
con encabezados como estos:
Access-Control-Allow-Origin: [the same origin from the request]
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]
Aquí hay un buen documento: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server
NOTA: No estoy seguro de que funcione con la última versión de Angular.
ORIGINAL:
También es posible anular la solicitud de OPCIONES (solo se probó en Chrome):
app.config([''$httpProvider'', function ($httpProvider) {
//Reset headers to avoid OPTIONS request (aka preflight)
$httpProvider.defaults.headers.common = {};
$httpProvider.defaults.headers.post = {};
$httpProvider.defaults.headers.put = {};
$httpProvider.defaults.headers.patch = {};
}]);