2009-06-25 13 views
26

Grails'te bir RESTful API uyguluyor ve isteğin gövdesini imzalamayı içeren özel bir kimlik doğrulama şeması kullanıyorum (Amazon'un S3 kimlik doğrulama şemasına benzer bir şekilde). Bu nedenle, isteği doğrulamak için dijital imzayı hesaplayıp doğrulamak için ham POST veya PUT gövdesi içeriğine erişmem gerekiyor.Bir PUT veya POST isteğinin ham gövdesine erişme

Denetleyicide bir önceki denetleyicide kimlik doğrulaması yapıyorum. Bu yüzden, isteksiz bir şekilde istemcinin engellemede erişilebilir olmasını istiyorum ve yine de gerçek eylemde request.JSON'u kullanabiliyorum. Alıcıyı getInputStream veya getReader (ServletRequest tarafından sağlanan yöntemler) kullanarak engelleyicide okursam korkarım ki, request.JSON yoluyla erişmeye çalıştığımda, bu eylemde beden boş görünecektir.

Django'dan Grails'e geçiş yapıyorum ve bir yıl önce Django'da da aynı sorunla karşılaştım, ancak hızlı bir şekilde yandı. Django, bu amaç için kullanabileceğiniz request.raw_post_data özniteliğini sağlar.

Son olarak, güzel ve RESTful olmak için POST ve PUT istekleri için çalışmak istiyorum.

Herhangi bir öneri veya işaretçi büyük takdir edilecektir. Varsa, hızlı ve kirli hackler için fikirler üzerinde zarif bir çözümün nasıl uygulanacağına dair işaretçileri tercih ederim. =) Django'da, istek için bazı özellikler eklemek üzere bazı orta yazılım isteği işleyicilerini düzenledim. Groovy ve Grails'e çok yeni geldim, bu yüzden bu kodun nerede yaşadığına dair hiçbir fikrim yok, ancak gerekirse aynı şeyi yapmayı düşünmüyorum.

+0

grails Django veri taşımak için bir sebep var: Burada harika bir örnek? – Divick

cevap

39

Bir Servlet Filtresinde HttpServletRequest'i geçersiz kılmakla mümkündür.

Sen depolayan bir HttpServletRequestWrapper uygulamak gerekir istek gövdesi: src/java/grails/util/http/MultiReadHttpServletRequest.java

package grails.util.http; 

import org.apache.commons.io.IOUtils; 

import javax.servlet.http.HttpServletRequestWrapper; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.ServletInputStream; 
import java.io.*; 
import java.util.concurrent.atomic.AtomicBoolean; 

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { 

    private byte[] body; 

    public MultiReadHttpServletRequest(HttpServletRequest httpServletRequest) { 
     super(httpServletRequest); 
     // Read the request body and save it as a byte array 
     InputStream is = super.getInputStream(); 
     body = IOUtils.toByteArray(is); 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     return new ServletInputStreamImpl(new ByteArrayInputStream(body)); 
    } 

    @Override 
    public BufferedReader getReader() throws IOException { 
     String enc = getCharacterEncoding(); 
     if(enc == null) enc = "UTF-8"; 
     return new BufferedReader(new InputStreamReader(getInputStream(), enc)); 
    } 

    private class ServletInputStreamImpl extends ServletInputStream { 

     private InputStream is; 

     public ServletInputStreamImpl(InputStream is) { 
      this.is = is; 
     } 

     public int read() throws IOException { 
      return is.read(); 
     } 

     public boolean markSupported() { 
      return false; 
     } 

     public synchronized void mark(int i) { 
      throw new RuntimeException(new IOException("mark/reset not supported")); 
     } 

     public synchronized void reset() throws IOException { 
      throw new IOException("mark/reset not supported"); 
     } 
    } 

} 

akım ServletRequest geçersiz kılan Servlet Filtre: src/java/grails/util/http/MultiReadServletFilter.java

package grails.util.http; 

import javax.servlet.*; 
import javax.servlet.http.HttpServletRequest; 
import java.io.IOException; 
import java.util.Set; 
import java.util.TreeSet; 

public class MultiReadServletFilter implements Filter { 

    private static final Set<String> MULTI_READ_HTTP_METHODS = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {{ 
     // Enable Multi-Read for PUT and POST requests 
      add("PUT"); 
      add("POST"); 
    }}; 

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 
     if(servletRequest instanceof HttpServletRequest) { 
      HttpServletRequest request = (HttpServletRequest) servletRequest; 
      // Check wether the current request needs to be able to support the body to be read multiple times 
      if(MULTI_READ_HTTP_METHODS.contains(request.getMethod())) { 
       // Override current HttpServletRequest with custom implementation 
       filterChain.doFilter(new MultiReadHttpServletRequest(request), servletResponse); 
       return; 
      } 
     } 
     filterChain.doFilter(servletRequest, servletResponse); 
    } 

    public void init(FilterConfig filterConfig) throws ServletException { 
    } 

    public void destroy() { 
    } 
} 

sonra grails install-templates çalıştırın ve src/templates/savaşta web.xml düzenleyip charEncodingFilter tanımı sonra bu eklemek gerekir:

request.inputStream Aramanızı istediğiniz sıklıkta arayabilirsiniz.

bu somut kodu/prosedürü test değil ama geçmişte benzer şeyler yaptık, bu yüzden ;-)

Not çalışması gerekir: büyük isteklerin uygulaması (OutOfMemory öldürebilir unutmayın. ..)

+1

Hala bu çalışmayı doğru şekilde göremiyorum. 1) Filtrelerin doğru şekilde ayarlandığını biliyorum, çünkü bir eylemde "istek", "[email protected]" dizgesel gösterimine sahiptir. 2) Wget --post-data = 'xxx' ve curl -d @file ile isteği oluşturmayı denedim ve isteğin Wireshark kullanılarak doğru şekilde oluşturulduğunu doğruladım. 3) Ancak, ne çalıştığım önemli değil, request.reader.readLine() öğesi null değerini döndürür. 4) Ayrıca, gövde bayt dizisini kullanan bir "bodyToString()" ekledim, ancak her zaman boş bir dize döndürüyor. Herhangi bir fikrin var mı? –

+0

Daha güvenilir çalışmak için multireadhttpservletrequest kodunu değiştirdim. Bunu denemek isteyebilirsiniz. –

+0

Harika çözüm! Hataları yakalarken ve hata raporu gönderirken bana yardımcı oldu. – Trick

25

olarak burada görülebilir

http://jira.codehaus.org/browse/GRAILS-2017

sadece XML otomatik işleme kontrolörleri metin erişilebilir hale getirir grails kapatarak.Bu

class EventsController { 

static allowedMethods = [add:'POST'] 

def add = { 
    log.info("Got request " + request.reader.text)  
    render "OK" 
}} 

iyisi, Anders

+3

Açıklığa kavuşturmak için, bunu denedim ve işe yarıyor, ancak grails.converters 'ı almamanız çok önemlidir. * (Muhtemelen,' XML'in otomatik işlenmesini otomatik olarak açma 'ile kastedilen nedir) – mikermcneil

+0

Graemes yorumunda olduğu gibi XML 'lerin otomatik kullanımı' http://jira.grails.org/browse/GRAILS-2017?focusedCommentId=46871&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-46871 –

1

Tek yolu POST istekleri için dere ve istek parametrelerine hem sürekli erişime sahip edebilmek için görünüyor gibi dere okuma geçersiz kılan bir sarmalayıcı yazmaktır yanı sıra parametre erişimi.

Modify HttpServletRequest body

İlgili konular