Sisk

Habilitando CORS (Compartilhamento de Recursos entre Origens) no Sisk

Esta página foi traduzida automaticamente do inglês. Ler o original

Sisk possui uma ferramenta que pode ser útil para lidar com Cross-Origin Resource Sharing (CORS) ao expor seu serviço publicamente. Esse recurso não faz parte do protocolo HTTP, mas é uma funcionalidade específica dos navegadores web definida pelo W3C. Esse mecanismo de segurança impede que uma página web faça requisições a um domínio diferente daquele que forneceu a página. Um provedor de serviço pode permitir que certos domínios acessem seus recursos, ou apenas um.

Mesma Origem #

Para que um recurso seja identificado como “mesma origem”, uma requisição deve indicar o cabeçalho Origin em sua solicitação:

HTTP
GET /api/users HTTP/1.1
Host: example.com
Origin: http://example.com
...

E o servidor remoto deve responder com um cabeçalho Access-Control-Allow-Origin contendo o mesmo valor da origem solicitada:

HTTP
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
...

Essa verificação é explícita: o host, a porta e o protocolo devem ser os mesmos da solicitação. Confira o exemplo:

  • Um servidor responde que seu Access-Control-Allow-Origin é https://example.com:
    • https://example.net - o domínio é diferente.
    • http://example.com - o esquema é diferente.
    • http://example.com:5555 - a porta é diferente.
    • https://www.example.com - o host é diferente.

Na especificação, apenas a sintaxe é permitida para ambos os cabeçalhos, tanto em requisições quanto em respostas. O caminho da URL é ignorado. A porta também é omitida se for a porta padrão (80 para HTTP e 443 para HTTPS).

HTTP
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>

Habilitando CORS #

Nativamente, você tem o objeto CrossOriginResourceSharingHeaders dentro do seu ListeningHost.

Você pode configurar o CORS ao inicializar o servidor:

C#
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UseCors(new CrossOriginResourceSharingHeaders(
            allowOrigin: "http://example.com",
            allowHeaders: ["Authorization"],
            exposeHeaders: ["Content-Type"]))
        .Build();

    await app.StartAsync();
}

O código acima enviará os seguintes cabeçalhos para todas as respostas:

HTTP
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization
Access-Control-Expose-Headers: Content-Type

Esses cabeçalhos precisam ser enviados em todas as respostas para um cliente web, incluindo erros e redirecionamentos.

Você pode notar que a classe CrossOriginResourceSharingHeaders possui duas propriedades semelhantes: AllowOrigin e AllowOrigins. Observe que uma está no plural, enquanto a outra está no singular.

  • A propriedade AllowOrigin é estática: apenas a origem que você especificar será enviada em todas as respostas.
  • A propriedade AllowOrigins é dinâmica: o servidor verifica se a origem da requisição está contida nesta lista. Se for encontrada, ela é enviada na resposta daquela origem.

Quando a origem permitida depende da requisição, isto é, quando você usa AllowOrigins ou o valor AutoAllowOrigin descrito abaixo, o Sisk também envia o cabeçalho Vary: Origin para que os caches não reutilizem uma resposta para uma origem diferente.

Curingas e cabeçalhos automáticos #

Alternativamente, você pode usar um curinga (*) na origem da resposta para especificar que qualquer origem tem permissão para acessar o recurso. Contudo, esse valor não é permitido para requisições que contenham credenciais (cabeçalhos de autorização) e essa operação resultará em um erro.

Você pode contornar esse problema listando explicitamente quais origens serão permitidas através da propriedade AllowOrigins ou também usar a constante AutoAllowOrigin no valor de AllowOrigin. Essa propriedade mágica definirá o cabeçalho Access-Control-Allow-Origin com o mesmo valor do cabeçalho Origin da requisição.

Você também pode usar AutoFromRequestMethod e AutoFromRequestHeaders para um comportamento semelhante ao AllowOrigin, que responde automaticamente com base nos cabeçalhos enviados.

C#
using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        
        // Responde com base no cabeçalho Origin da requisição
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        
        // Responde com base no cabeçalho Access-Control-Request-Method ou no método da requisição
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],

        // Responde com base no cabeçalho Access-Control-Request-Headers ou nos cabeçalhos enviados
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],

        exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
        allowCredentials: true))
    .Build();

Outras Formas de Aplicar CORS #

Se você está lidando com service providers, pode sobrescrever valores definidos no arquivo de configuração:

C#
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UsePortableConfiguration(...)
        .UseCors(cors => {
            // Substituirá a origem definida na configuração
            // do arquivo.
            cors.AllowOrigin = "http://example.com";
        })
        .Build();

    await app.StartAsync();
}

Desabilitando CORS em Rotas Específicas #

A propriedade UseCors está disponível tanto para rotas quanto para todos os atributos de rota e pode ser desativada com o exemplo a seguir:

C#
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    // GET /api/widgets/colors
    [RouteGet("/colors", UseCors = false)]
    public IEnumerable<string> GetWidgets() {
        return new[] { "Green widget", "Red widget" };
    }
}

Substituindo Valores na Resposta #

Você pode substituir ou remover valores explicitamente em uma ação de roteador:

C#
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    public IEnumerable<string> GetWidgets(HttpRequest request) {

        // Remove o cabeçalho Access-Control-Allow-Credentials
        request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
        
        // Substitui o cabeçalho Access-Control-Allow-Origin
        request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";

        return new[] { "Green widget", "Red widget" };
    }
}

Requisições Preflight #

Uma requisição preflight é uma requisição do método OPTIONS que o cliente envia antes da requisição real.

Quando uma requisição com o método OPTIONS corresponde ao caminho de uma rota, mas nenhuma rota está configurada explicitamente para OPTIONS, o servidor Sisk responde com um 200 OK e os cabeçalhos CORS aplicáveis, e então o cliente pode prosseguir com a requisição real.

  • Se uma rota estiver mapeada explicitamente com RouteMethod.Options, por exemplo com MapOptions, essa rota trata a requisição em vez da resposta automática.
  • Se a rota que corresponde ao caminho tem UseCors = false, a resposta automática ainda é enviada, porém sem os cabeçalhos CORS.

Nota

Antes do Sisk 1.7, a resposta automática de preflight sempre continha os cabeçalhos CORS, mesmo para rotas com UseCors = false.

Desabilitando CORS Globalmente #

Não é possível fazer isso. Para não usar CORS, simplesmente não o configure.

Digite para pesquisar na documentação e na referência da API.