REST com Http OPTIONS no VRaptor

1,808 views
Skip to first unread message

Rafael Dipold

unread,
Apr 18, 2013, 10:19:30 AM4/18/13
to caelum-...@googlegroups.com
Olá pessoal,

O método Http OPTIONS é utilizado quando quer-se obter informações dos métodos/verbos Http disponíveis para comunicação com o servidor?

Se sim, como se implementa isso no VRaptor? Isso é automático uma vez que o VRaptor já sabe quais métodos GET/POST/PUT/DELETE há no controller? 

Digamos que eu tenha uma entidade Estado com os seguintes métodos REST:

@Get("/pais/{pais.id}/estado") //Retorna List<Estado>
@Get("/estado/{estado.id}") //Retorna Estado
@Post("/estado") //Salva ou atualiza um Estado
@Delete("/estado/{estado.id}")  //Remove um Estado

Tentei adicionar a esse controller o método abaixo:

@Options("/estado/options")
    public void options() {
        Status resultStatus = result.use(Results.status());
        resultStatus.header("Access-Control-Allow-Origin", "*");        //Objetivo aqui é permitir o acesso aos métodos acima via Cross-Domain
        resultStatus.noContent();
    }

Mas ao acessar /estado/options obtenho um 405 Method Not Allowed

Alguém poderia me dar uma luz?

Grato desde sempre,

Otávio Garcia

unread,
Apr 18, 2013, 11:56:51 AM4/18/13
to caelum-...@googlegroups.com
Se você acessar OPTIONS pelo navegador, ele irá enviar na verdade um GET, então receberá um 405. Você tentou acessar por um cliente REST que suporte OPTIONS?

2013/4/18 Rafael Dipold <dip...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "caelum-vraptor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to caelum-vrapto...@googlegroups.com.
To post to this group, send email to caelum-...@googlegroups.com.
Visit this group at http://groups.google.com/group/caelum-vraptor?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rafael Dipold

unread,
Apr 18, 2013, 3:32:56 PM4/18/13
to caelum-...@googlegroups.com
Então, não tenho um cliente REST pra fazer o teste, na verdade ainda não descobri como testar essa função.

Eu estava lendo sobre CORS e Hipermídia em REST, e quis fazer uns testes, apenas para estudo de caso.

Eu havia entendido que ao realizar uma requisição cross-domain, o navegador faz automaticamente uma requisição OPTIONS (por isso minha tentativa de implementá-lo), para saber se está autorizado para tal e quais métodos o servidor disponibiliza. E que um client REST deveria fazer o mesmo.

Mas essa requisição nunca chegou ao meu servidor, então removi o método OPTIONS e fiz um interceptor autorizar todas requisições:
response.setHeader("Access-Control-Allow-Origin", "*");
stack.next(method, resourceInstance);

E passou a funcionar requisição cross-domain.

Mas minha dúvida ainda persiste: ql o objetivo do OPTIONS? Se eu quero desenvolver um servidor REST seguindo a risca a API RestFul, eu devo implementar o Http OPTIONS?



Rafael Dipold
dip...@gmail.com

Lucas Cavalcanti

unread,
Apr 18, 2013, 3:37:25 PM4/18/13
to caelum-vraptor
O VRaptor já implementa o OPTIONS e ele retorna os métodos allowed pra aquela URL:

$ curl -i -XOPTIONS http://meu-sistema/uma-url

HTTP/1.1 200 OK
Server: nginx/1.2.1
Date: Thu, 18 Apr 2013 19:38:07 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Allow: GET

vc pode incluir qualquer Header nessa resposta como o Access-Control-Allow-Origin, para o cliente saber o que pode fazer nessa url.


2013/4/18 Rafael Dipold <dip...@gmail.com>

Rafael Dipold

unread,
May 6, 2013, 9:58:24 AM5/6/13
to caelum-...@googlegroups.com
Estou com um probleminha aqui ainda e acho que pode ser algo específico do VRaptor. 

Tenho um projeto servidor VRaptor rodando no Tomcat, e um projeto cliente (html+css+js) rodando no nginx.

Tenho os métodos GET e POST abaixo implementados e funcionando em cross-domain:

@Get("/pais/{pais.id}/estado") //Retorna List<Estado>
@Get("/estado/{estado.id}") //Retorna Estado
@Post("/estado") //Salva ou atualiza um Estado

Tenho tb o método @Delete, mas ao fazer uma chamada HTTP DELETE via js:

Request Method:OPTIONS
Status Code:200 OK

Request Headers
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4
Access-Control-Request-Headers:accept, origin, content-type
Access-Control-Request-Method:DELETE
Connection:keep-alive
Host:localhost:8080
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31

Response Headers
Allow:GET, DELETE
Content-Length:0
Date:Mon, 06 May 2013 12:22:13 GMT
Server:Apache-Coyote/1.1

Aparentemente o nginx tranforma a requisição HTTP DELETE em HTTP OPTIONS. O VRaptor aparentemente responde corretamente, mas no log sempre dá uma exceção:
09:22:13,089 DEBUG [DefaultResourceTranslator] trying to access /estado/11
09:22:13,090 DEBUG [ResourceLookupInterceptor] Method OPTIONS is not allowed for requested URI. Allowed Methods are [GET, DELETE]
br.com.caelum.vraptor.http.route.MethodNotAllowedException: Method OPTIONS is not allowed for requested URI. Allowed Methods are [GET, DELETE]

Tenho um interceptor alterando o header de todas as requisições para:
response.setHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");

E é por causa dele que os métodos GET/POST funcionam, mas no caso da requisição acima, esse interceptor nunca é chamado.

Tentei então implementar o @Options abaixo no mesmo controller, para resolver o MethodNotAllowedException porém tb nunca é chamado e o VRaptor continua dando a mesma exception:
@Options("/estado/{estado.id}")
    public void options(Estado estado) {
        Status resultStatus = result.use(Results.status());
        resultStatus.header("Access-Control-Allow-Origin", "*");
        resultStatus.noContent();
    }

Tem alguma coisa errada na minha implementação do Options para o VRaptor estar ignorando-o?

Marcelo Fabricio de Mello

unread,
May 6, 2013, 10:07:34 AM5/6/13
to caelum-...@googlegroups.com
O que vc está querendo são chamadas utilizando a
métodologia ( se é que posso chamar assim ) CORS, certo?

www.corsproxy.com






--

Rafael Dipold

unread,
May 6, 2013, 10:24:16 AM5/6/13
to caelum-...@googlegroups.com
Sim, por isso permito o acesso de qlqr origem ao meu server (Access-Control-Allow-Origin : *) e os métodos GET e POST funcionam corretamente.

Mas o VRaptor está se comportanto estranho qt ao método OPTIONS, que é chamado quando faço um http DELETE.

O CORS Proxy é apenas para burlar a restrição para domínios que possuem políticas de bloqueio para same-origin. Na prática ele finge/imita um acesso pelo browser.

Rafael Dipold
dip...@gmail.com



2013/5/6 Marcelo Fabricio de Mello <marcel...@gmail.com>

--
You received this message because you are subscribed to a topic in the Google Groups "caelum-vraptor" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/caelum-vraptor/TXlt3z0iXzg/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to caelum-vrapto...@googlegroups.com.

Rafael Dipold

unread,
May 6, 2013, 10:40:13 AM5/6/13
to caelum-...@googlegroups.com
Meu entendimento é que a resposta padrão do http OPTIONS do VRaptor responde um:
Allow: GET, DELETE

Mas no caso de permitir acesso via Cross Domain, ele tb deveria possuir os headers:
Access-Control-Allow-Origin:*
Access-Control-Allow-Methods:GET, DELETE

Para resolver ou teria que sobreescrever a resposta padrão que o VRaptor implementa para chamada ao OPTIONS, ou o VRaptor deveria fazer a leitura do meu método @options 



Rafael Dipold
dip...@gmail.com



2013/5/6 Rafael Dipold <dip...@gmail.com>

Lucas Cavalcanti

unread,
May 6, 2013, 8:15:09 PM5/6/13
to caelum-vraptor
Ele não tá lendo o @Options?


2013/5/6 Rafael Dipold <dip...@gmail.com>

Rafael Dipold

unread,
May 6, 2013, 8:50:30 PM5/6/13
to caelum-...@googlegroups.com
Não consegui fazer ler o @options até o momento. Nem o interceptor executa mais qd tenta dar um HTTP OPTIONS via cross domain. A exception ocorre antes.
 
Pare resolver temporariamente implementei um Filter no lugar do Interceptor acima. 

Rafael Dipold
dip...@gmail.com



2013/5/6 Lucas Cavalcanti <lucasm...@gmail.com>

Rafael Dipold

unread,
May 7, 2013, 9:08:56 AM5/7/13
to caelum-...@googlegroups.com
Caso isso ajude:

Para uma chamada HTTP OPTIONS na URI /estado/11

O método routesMatchingUri(String uri) da classe DefaultRouter retorna:

[[FixedMethodStrategy: /estado/{estado.id} EstadoController.get(Estado) [GET]], 
 [FixedMethodStrategy: /estado/{estado.id} EstadoController.delete(Estado) [DELETE]]]

mas meu controller possui:

@Get("/estado/{estado.id}") public void get(Estado estado)
@Delete("/estado/{estado.id}") public void delete(Estado estado)
@Options("/estado/{estado.id}") public void options(Estado estado)

Fazendo com que o método routesMatchingUriAndMethod(String uri, HttpMethod method) que chama o método acima gere a exceção MethodNotAllowedException

Acredito que a intenção foi fazer com que o método OPTIONS não responda que ele mesmo é um método permitido

Rafael Dipold
dip...@gmail.com

Lucas Cavalcanti

unread,
May 7, 2013, 10:14:44 AM5/7/13
to caelum-vraptor
Acho que isso é um bug mesmo... =(

registra isso lá no https://github.com/caelum/vraptor/issues por favor?


--

Marcelo Fabricio de Mello

unread,
May 20, 2013, 10:47:48 AM5/20/13
to caelum-...@googlegroups.com
Uma possível solução para disponibilizar o CORS ( Cross-Origin Resource Sharing ).

http://software.dzhuvinov.com/cors-filter-installation.html




2013/5/7 Lucas Cavalcanti <lucasm...@gmail.com>

Vinicius Maia

unread,
May 20, 2013, 6:46:50 PM5/20/13
to caelum-...@googlegroups.com
Não seria algo relacionado a isso....   *_method* ... ?

<form action="/cliente" method="post">
    <input name="cliente.id" value="5" type="hidden" />
    <button type="submit" name="_method" value="DELETE">remover cliente 5</button>
</form>

Marcelo Fabricio de Mello

unread,
May 20, 2013, 8:53:18 PM5/20/13
to caelum-...@googlegroups.com
CORS tem a ver com requisições para domínios diferentes.

Por exemplo, se vc quiser buscar no twitter por uma determinada palavra utilizando a URL:

Com ajax vc só pode realizar requisições para seu próprio domínio:


https://servidor.com.br/pagina/ - protocolo diferente 
http://www.servidor.com.br/pagina/ - domínio diferente 

Aí o pessoal criou do tal do jsonP, que com a técnica de inserir uma tag script dinamicamente e implementando a função que está na callback faz com que vc possa trazer o conteúdo do domínio diferente.


Já o CORS, é realizada umas configurações, vide (http://software.dzhuvinov.com/cors-filter.html) servidor da aplicação, pois o proprietário e um site, do domínio XYZ pode premitir acesso a determinadas URLs ou dentro do seu site (sinistro).

A técnica baseia-se, em uma troca de cabeçalhos entre o navegador e o servidor usando o método HTTP OPTIONS, antes da requisição real com GET ou POST. Nessa requisição OPTIONS o navegador envia um header "Origin" e espera por um header "Access-Control-Allow-Origin" com o mesmo valor ou com o valor "*". 

Se o header "Access-Control-Allow-Origin" contiver um dos valores esperados o navegador em seguida faz a requisição GET ou POST.


Resumindo, seria uma foram de ter acesso a conteúdo disponibilizado por domínios diferentes, utilizando AJAX sem utilizar a técnica do JSONP.


E este pessoal tem a solução:





Vinicius Maia

unread,
May 20, 2013, 9:54:31 PM5/20/13
to caelum-...@googlegroups.com
OK , entendi. Obrigado!!

Rafael Dipold

unread,
Aug 2, 2013, 2:26:48 PM8/2/13
to caelum-...@googlegroups.com
Caso alguém passe por aqui com problemas de acesso Cross-Domain (CORS) com VRaptor segue solução:

Basicamente a solução é acrescentar o Header abaixo para todas as requisições HTTP (inclusive OPTIONS):
Access-Control-Allow-Origin:*
Onde * libera requisições de qualquer origem. No lugar do * pode ser um ou mais domínios em específico (mais seguro).

E nas requisições HTTP OPTIONS, além do Header acima, é necessário incluir tb:
Access-Control-Allow-Methods: [GET, POST, OPTIONS]
Access-Control-Allow-Headers: Content-Type, accept, authorization, origin

Inicialmente eu havia feito um Filter e um Interceptor para tratar dessa questão.
O interceptor sozinho não resolve o problema porque o VRaptor responde automaticamente a requisições HTTP OPTIONS e o Interceptor nunca é chamado.
Com o Filter não consigo saber quais métodos de fato deveriam ser permitidos para determinada URI, o que me forçava a liberar sempre todos. 

Hoje estava refatorando o código e achei mais prático substituir o Filter por um controller:

@Resource
public class CORSController {
private final Result result;
private final Router router;
private final RequestInfo requestInfo;

public CORSController(Result result, Router router, RequestInfo requestInfo) {
this.result = result;
this.router = router;
this.requestInfo = requestInfo;
}
@Options @Path("/*")
public void options() {
Set<HttpMethod> allowed = router.allowedMethodsFor(requestInfo.getRequestedUri());
      result.use(Results.status()).header("Allow", allowed.toString().replaceAll("\\[|\\]", ""));        
      result.use(Results.status()).header("Access-Control-Allow-Origin", "*");        
      result.use(Results.status()).header("Access-Control-Allow-Methods", allowed.toString().replaceAll("\\[|\\]", ""));        
      result.use(Results.status()).header("Access-Control-Allow-Headers", "Content-Type, accept, authorization, origin");        
      result.use(Results.status()).noContent();
}
}

Dessa forma tenho maior controle sobre a requisição, o que me permitiu dinamizar qualquer um dos headers tal qual fiz com o Access-Control-Allow-Methods.

A desvantagem dessa abordagem é que ainda preciso do interceptor para adicionar o Access-Control-Allow-Origin as requisições dos outros métodos HTTP (GET, POST, etc).
Outra desvatagem é que em agora todos interceptors existentes no projeto serão executados quando receberem uma requisição HTTP OPTIONS, e dependendo do que seu interceptor faz, talvez você tenha que tratar isso. Por exemplo, ocorreu comigo que não queria que o interceptor que valida as credenciais de acesso fosse executado qd com requisições HTTP OPTIONS.

P.S.: @Options("/*") não funciona na atual versão do VRaptor. Já encaminhei Pull Request pra resolver https://github.com/caelum/vraptor/pull/559

Enfim.. é isso.. #FikADik

Fernando Raposo

unread,
Aug 1, 2014, 1:18:50 AM8/1/14
to caelum-...@googlegroups.com
Olá!

Eu usei essa solução no VRaptor 3. Agora estou migrando para o 4.1.0 e o RequestInfo não existe mais. Alguém já resolveu isto de outra forma?

Obrigado.

Rafael Dipold

unread,
Aug 2, 2014, 9:36:45 AM8/2/14
to caelum-...@googlegroups.com
Existe sim:

@Controller
public class CORSController {

@Inject private Result result;
@Inject private Router router;
@Inject private RequestInfo requestInfo;

@Options("/*")
public void options() {
Set<HttpMethod> allowed = router.allowedMethodsFor(requestInfo.getRequestedUri());
String allowMethods = allowed.toString().replaceAll("\\[|\\]", "");

        result.use(Results.status()).header("Allow", allowMethods);
        result.use(Results.status()).header("Access-Control-Allow-Methods", allowMethods);
        result.use(Results.status()).header("Access-Control-Allow-Headers", "Content-Type, accept, Authorization, X-Tenant, X-Filial, origin");

        result.use(Results.status()).noContent();
}
}

Rafael Dipold
dip...@gmail.com



--
You received this message because you are subscribed to a topic in the Google Groups "caelum-vraptor" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/caelum-vraptor/TXlt3z0iXzg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to caelum-vrapto...@googlegroups.com.

To post to this group, send email to caelum-...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages