【问题标题】:How to manipulate a RequestHeader in Play2 before the Controller works on it如何在控制器处理之前在 Play2 中操作 RequestHeader
【发布时间】:2014-05-22 14:21:29
【问题描述】:

我想添加一个 trackingId 来跟踪 Play2 中的请求和从 WebApp 中调用的服务。 在Controller处理请求之前,应该可以将tracingId添加到请求中。

到目前为止,我认为这可以通过过滤器来完成。

object AddTraceIdFilter extends Filter  {
    override def apply(next: (RequestHeader) => Result)(rh: RequestHeader): Result = {

    val traceId = TraceId.fromRequestHeader(rh) getOrElse addAnTraceId
    next(rh).withHeaders( TraceId.traceKey -> traceId )
  }
}

但是通过这种方法,标头附加到结果上,并且我认为请求没有被触及。

在控制器中我想做类似的事情

val traceId = traceIdfromReuest(request)

有没有办法为每个传入的请求修改 RequestHeader 以在控制器获取它之前添加一个 traceId-Header ? 我已经看过 Interceptors http://www.playframework.com/documentation/2.0/ScalaInterceptors 但我没有发现如何在那里操作请求标头。

【问题讨论】:

    标签: scala playframework playframework-2.1


    【解决方案1】:

    你应该从rh构造一个新的RequestHeader

    val rhWithTraceId = rh....
    

    例如:

    val rhWithTraceId = rh.copy(tags = rh.tags + ("traceId" -> traceId))
    

    然后你就可以使用它的所有下一个功能

    next(rhWithTraceId)
    

    在您的控制器中,您可以从

    访问 traceId
    request.tags.get("traceId")
    

    另一种解决方案是使用ActionBuilder

    【讨论】:

    • 为解决方案添加了一些说明。谢谢你的回答:)
    【解决方案2】:

    正如@Yann Simon 所说,最简单的解决方案是使用 tags 而不是修改请求中的 http-headers

    如果您真的想修改 RequestHeader 中的 http-header,您可以执行以下操作:

    oldHeader.copy(
      headers =
        new Headers{
          val data : Seq[(String, Seq[String])]  = 
             oldHeader.headers.toMap.toSeq ++ additionalKeys.mapValues(Seq(_))
        }
    )
    

    要记住的重要部分是,如果您想在过滤器中修改 RequestHeader 中的数据,这一切都归结为提供给带有签名(RequestHeader) => Result 的方法的参数,通常称为 next 在示例中。

    object AddTraceIdFilter extends Filter  {
      override def apply(next: (RequestHeader) => Result)(rh: RequestHeader): Result = {
    
        val traceId = TraceId.fromRequestHeader(rh) getOrElse addAnTraceId
        next(
          rh.copy(tags = tags + ("traceId" -> traceId)) // <- Headers modified here will be readable from Reuest and the next filters
        ).withHeaders( "traceId" -> traceId ) // <- This just sets the httpHeaders for the Result given to the User after the filter has been applied, the values can not be read from within a controller
      }
    }
    

    【讨论】:

      【解决方案3】:

      首先我想直接回答这个问题,对于 Java 人来说:

      这是您可以使用过滤器在请求标头触及控制器之前对其进行操作的方法:

      class TracingFilterAction extends Action<TracingFilter> {
      
          @Override
          public Promise<SimpleResult> call(Context ctx) throws Throwable {
      
              ctx.request().headers().put(key, value)
      

      自从你问了一个关于“跟踪”请求的问题,我有分享我的 LoggingFilter 的冲动。

      对于 Play Framework Java 社区,我开发了一个出色的 LoggingFilter,它将记录完整的请求、标头和负载。

      对于 Scala 社区,它应该仍然适合您,但您将不得不在您的 Scala 项目中使用一些额外的 Java 代码:(

      用法

      这将启用类级别的请求日志记录

      @LoggingFilter
      public final class MyController extends Controller {   
      
      }
      

      这将在方法级别启用请求日志记录

      @LoggingFilter
      public Result index() {
          ...
      }
      

      这是 2.2.x(可能还有 2.2.3)的实现


      import java.io.UnsupportedEncodingException;
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      import java.net.URLEncoder;
      import java.util.Map;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.atomic.AtomicLong;
      
      import org.apache.commons.lang3.StringUtils;
      
      import play.Logger;
      import play.Logger.ALogger;
      import play.libs.F.Function0;
      import play.libs.F.Promise;
      import play.mvc.Action;
      import play.mvc.Http.Context;
      import play.mvc.Http.Request;
      import play.mvc.SimpleResult;
      import play.mvc.With;
      
      @With(LoggingFilterAction.class)
      @Target({ ElementType.TYPE, ElementType.METHOD })
      @Retention(RetentionPolicy.RUNTIME)
      public @interface LoggingFilter {
      
      }
      
      class LoggingFilterAction extends Action<LoggingFilter> {
      
          private static final String NOTIFICATION_PREFIX = "* ";
          private static final String REQUEST_PREFIX = "> ";
          private static final String RESPONSE_PREFIX = "< ";
      
          private final ALogger logger = Logger.of(getClass());
          private final AtomicLong _id = new AtomicLong(0);
          private final String newLine = System.getProperty("line.separator");
      
          @Override
          public Promise<SimpleResult> call(Context ctx) throws Throwable {
              long id = _id.incrementAndGet();
              StringBuilder b = new StringBuilder();
              printRequestLine(b, id, ctx.request().method(), ctx.request().uri());
              printRequestHeaders(b, id, REQUEST_PREFIX, ctx.request().headers());
              String requestContent = contentAsString(ctx.request(), b);
              if (!StringUtils.isEmpty(requestContent)) {
                  b.append(requestContent);
              }
              b.append(newLine);
              final SimpleResult result = delegate.call(ctx).get(5, TimeUnit.SECONDS);
              printResponseLine(b, id, getStatus(result));
              printResponseHeaders(b, id, RESPONSE_PREFIX, headers(result));
              String responseContent = contentAsString(result);
              if (!StringUtils.isEmpty(responseContent)) {
                  b.append(responseContent);
              }
              b.append(newLine);
              logger.debug(b.toString());
      
              return Promise.promise(new Function0<SimpleResult>() {
                  public SimpleResult apply() throws Throwable {
                      return result;
                  }
              });
          }
      
          private String header(String header, SimpleResult result) {
              return play.core.j.JavaResultExtractor.getHeaders(result).get(header);
          }
      
          private String charset(SimpleResult result) {
              String h = header("Content-Type", result);
              if (h == null) return null;
              if (h.contains("; charset=")) {
                  return h.substring(h.indexOf("; charset=") + 10, h.length()).trim();
              }
              else {
                  return null;
              }
          }
      
          private byte[] contentAsBytes(SimpleResult result) {
              return play.core.j.JavaResultExtractor.getBody(result);
          }
      
          private String contentAsString(Request request, StringBuilder b) {
              try {
                  if (request.body() == null) {
                      return "request has no body";
                  }
              } catch (ClassCastException e) {
                  return "request has no body";
              }
              if (request.body().asText() != null) {
                  logger.debug("getting request body as text");
                  return request.body().asText();
              }
              if (request.body().asRaw() != null) {
                  logger.debug("getting request body as raw");
                  try {
                      return new String(request.body().asRaw().asBytes(), "utf-8");
                  }
                  catch (UnsupportedEncodingException e) {
                      throw new RuntimeException(e);
                  }
              }
              if (request.body().asJson() != null) {
                  logger.debug("getting request body as json");
                  return request.body().asJson().toString();
              }
              if (request.body().asFormUrlEncoded() != null) {
                  logger.debug("getting request body as as formUrlEncoded");
                  return toString(request.body().asFormUrlEncoded());
              }
              if (request.body().asMultipartFormData() != null) {
                  logger.debug("getting request body as multipartFormData");
                  return request.body().asMultipartFormData().toString();
              }
              if (request.body().asXml() != null) {
                  logger.debug("getting request body as xml");
                  return request.body().asXml().toString();
              }
              logger.debug("getting request body as default");
              return request.body().toString();
          }
      
          private String toString(Map<String, String[]> data) {
              StringBuilder sb = new StringBuilder();
              for (Map.Entry<String, String[]> e : data.entrySet()) {
                  String[] val = e.getValue();
                  String key = e.getKey();
                  if (sb.length() > 0) {
                      sb.append("&");
                  }
                  try {
                      sb.append(key).append("=").append(URLEncoder.encode(val[0], "utf-8"));
                  }
                  catch (UnsupportedEncodingException e1) {
                      throw new RuntimeException(e1);
                  }
              }
              return sb.toString();
          }
      
          private String contentAsString(SimpleResult result) {
              try {
                  String charset = charset(result);
                  if (charset == null) {
                      charset = "utf-8";
                  }
                  return new String(contentAsBytes(result), charset);
              }
              catch (RuntimeException e) {
                  throw e;
              }
              catch (Throwable t) {
                  throw new RuntimeException(t);
              }
          }
      
          private Map<String, String> headers(SimpleResult result) {
              return play.core.j.JavaResultExtractor.getHeaders(result);
          }
      
          private int getStatus(SimpleResult result) {
              return result.getWrappedSimpleResult().header().status();
          }
      
          private StringBuilder prefixId(StringBuilder b, long id) {
              b.append(Long.toString(id)).append(" ");
              return b;
          }
      
          private void printRequestLine(StringBuilder b, long id, String method, String uri) {
              prefixId(b, id).append(NOTIFICATION_PREFIX)
                      .append("LoggingFilter - Request received on thread ")
                      .append(Thread.currentThread().getName()).append("\n");
              prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" ").append(uri).append("\n");
          }
      
          private void printResponseLine(StringBuilder b, long id, int status) {
              prefixId(b, id).append(NOTIFICATION_PREFIX)
                      .append("LoggingFilter - Response received on thread ")
                      .append(Thread.currentThread().getName()).append("\n");
              prefixId(b, id).append(RESPONSE_PREFIX).append(Integer.toString(status)).append("\n");
          }
      
          private void printResponseHeaders(StringBuilder b, long id, final String prefix,
                  Map<String, String> headers) {
              for (Map.Entry<String, String> e : headers.entrySet()) {
                  String val = e.getValue();
                  String header = e.getKey();
                  printPrefixedHeader(b, id, prefix, header, new String[] { val });
              }
      
          }
      
          private void printRequestHeaders(StringBuilder b, long id, final String prefix,
                  Map<String, String[]> headers) {
              for (Map.Entry<String, String[]> e : headers.entrySet()) {
                  String[] val = e.getValue();
                  String header = e.getKey();
                  printPrefixedHeader(b, id, prefix, header, val);
              }
          }
      
          private void printPrefixedHeader(StringBuilder b, long id, final String prefix, String header,
                  String[] val) {
              if (val.length == 1) {
                  prefixId(b, id).append(prefix).append(header).append(": ").append(val[0]).append("\n");
              }
              else {
                  StringBuilder sb = new StringBuilder();
                  boolean add = false;
                  for (Object s : val) {
                      if (add) {
                          sb.append(',');
                      }
                      add = true;
                      sb.append(s);
                  }
                  prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString())
                          .append("\n");
              }
          }
      
      }
      

      添加基于读取布尔配置属性来禁用日志记录的功能应该是一个简单的练习。

      然后在生产/登台/任何模式下,您可以轻松禁用 LoggingFilter。

      干杯!

      【讨论】:

        猜你喜欢
        • 2011-07-13
        • 1970-01-01
        • 2015-06-21
        • 1970-01-01
        • 1970-01-01
        • 2013-08-16
        • 2012-08-23
        • 1970-01-01
        • 2021-08-07
        相关资源
        最近更新 更多