Skip to content

关于 fit-framework HTTP 中 Header 处理的优化 #329

@Mai-icy

Description

@Mai-icy

功能摘要 / Feature Summary

优化fit-framework中的Header设计

功能类型 / Feature Type

全新功能 / New Feature

优先级 / Priority

高 - 非常需要这个功能 / High - Really need this feature

问题描述 / Problem Description

关于fit-framework当前的Header逻辑

modelengine.fit.http.header.HeaderValue

public interface HeaderValue {
    /**
     * 获取消息头的值。
     *
     * @return 表示消息头值的 {@link String}。
     */
    String value();

    /**
     * 获取所有参数集合。
     *
     * @return 表示所有参数集合的 {@link ParameterCollection}。
     */
    ParameterCollection parameters();

当前的Header值类例如 ContentType 都继承于 HeaderValue ,HeaderValue统一了一套基于value+parameters的模式,是由于例如Content-TypeContent-DispositionAccept 等都在各自的RFC中定义了这一套格式。

但是在[RFC 9110]§5.5中定义field-value:

  field-value    = *field-content
  field-content  = field-vchar
                   [ 1*( SP / HTAB / field-vchar ) field-vchar ]
  field-vchar    = VCHAR / obs-text
  obs-text       = %x80-FF

仅定义了字符要求,并没有严格统一的格式。各自Header的格式都在自己的规范中定义。

例如 User-Agent 的 [RFC7321]中说明:

User-Agent = product *( RWS ( product / comment ) )
product = token ["/" product-version]
product-version = token

允许以下的值

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...

使得当前的Header中固定要求的value+parameters模式很难统一和继承。

建议的解决方案 / Proposed Solution

Spring HttpHeaders + Tomcat中的设计

Spring是依赖于servlet的设计,因此许多关于Header的解析代码都依赖于servlet容器。

servlet标准要求容器提供了以下的接口方法:

public interface HttpServletRequest extends ServletRequest {
    String BASIC_AUTH = "BASIC";
    String FORM_AUTH = "FORM";
    String CLIENT_CERT_AUTH = "CLIENT_CERT";
    String DIGEST_AUTH = "DIGEST";

    String getAuthType();

    Cookie[] getCookies();

    long getDateHeader(String var1);

    String getHeader(String var1);

    Enumeration<String> getHeaders(String var1);

    Enumeration<String> getHeaderNames();

    int getIntHeader(String var1);
}

Tomcat的行为

Tomcat会对接收到的Header进行 field-name : field-value 依照[RFC 9110]

将得到的数据放入 MimeHeaders 类似于 Map<header-key, header-value>。

然后Tomcat会对自己的例如Host,Connection,Content-Length,Transfer-Encoding,Expect等协议层必须依赖的header字段进行解析,剩余的header字段仅保存字节流。

其他的Header主要采用懒加载的方式,在相应的getHeader触发的时候,进行解析成上层类并进行缓存处理,后续调用返回缓存。

Spring的行为

Spring又自行定义了许多上层类。
HttpHeaders implements MultiValueMap<String, String>
然后分别写 getter,setter针对每个Header进行单独序列化和反序列化。(下为片段

org.springframework.http.HttpHeaders

  public void setAccept(List<MediaType> acceptableMediaTypes) {
      this.set("Accept", MediaType.toString(acceptableMediaTypes));
  }

  public List<MediaType> getAccept() {
      return MediaType.parseMediaTypes(this.get("Accept"));
  }

  public void setAcceptLanguage(List<Locale.LanguageRange> languages) {
      Assert.notNull(languages, "LanguageRange List must not be null");
      DecimalFormat decimal = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS);
      List<String> values = languages.stream().map((range) -> range.getWeight() == (double)1.0F ? range.getRange() : range.getRange() + ";q=" + decimal.format(range.getWeight())).toList();
      this.set("Accept-Language", this.toCommaDelimitedString(values));
  }

  public List<Locale.LanguageRange> getAcceptLanguage() {
      String value = this.getFirst("Accept-Language");
      return StringUtils.hasText(value) ? LanguageRange.parse(value) : Collections.emptyList();
  }

  public void setAcceptLanguageAsLocales(List<Locale> locales) {
      this.setAcceptLanguage(locales.stream().map((locale) -> new Locale.LanguageRange(locale.toLanguageTag())).toList());
  }

  public List<Locale> getAcceptLanguageAsLocales() {
      List<Locale.LanguageRange> ranges = this.getAcceptLanguage();
      return ranges.isEmpty() ? Collections.emptyList() : ranges.stream().map((range) -> Locale.forLanguageTag(range.getRange())).filter((locale) -> StringUtils.hasText(locale.getDisplayName())).toList();
  }

Spring 的 HttpHeaders 是依赖 servlet 提供的原始 Header 数据,但完全独立于底层的解析逻辑。

修改方案:

是否可以参考 Spring 的做法:底层统一存 String,按需解析到上层对象,而不是在 HeaderValue 接口里强行统一格式。

替代方案 / Alternative Solutions

No response

确认事项 / Confirmations

  • 我已经搜索了现有的 issues 和讨论,确认这不是重复建议
    I have searched existing issues and discussions, confirming this is not a duplicate suggestion

  • 这个功能符合项目的目标和范围
    This feature aligns with the project's goals and scope

  • 我理解这个功能可能需要时间来实现
    I understand this feature may take time to implement

  • 我愿意协助实现这个功能 (可选)
    I'm willing to help implement this feature (optional)

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions