使用Fetch API读取响应标头

时间:2017-04-11 11:37:22

标签: javascript google-chrome-extension

我在Google Chrome扩展程序中拥有"*://*/*"的权限,我正在尝试从XMLHttpRequest切换到Fetch API

扩展存储用户输入的登录数据,这些数据曾经直接放入XHR的HTTP Auth的open()调用中,但是在Fetch下不能再直接用作参数。对于HTTP Basic Auth,绕过此限制是微不足道的,因为您可以手动设置授权标头:

fetch(url, {
  headers: new Headers({ 'Authorization': 'Basic ' + btoa(login + ':' + pass) })
  } });
然而,

HTTP Digest Auth需要更多互动;您需要读取服务器通过其401响应发送给您的参数以创建有效的授权令牌。我尝试使用此代码段阅读WWW-Authenticate响应标头字段:

fetch(url).then(function(resp) {
  resp.headers.forEach(function(val, key) { console.log(key + ' -> ' + val); });
}

但我得到的只是输出:

content-type -> text/html; charset=iso-8859-1

根据Chrome的开发人员工具,这本身就是正确的,但仍然缺少大约6个字段。如果我使用resp.headers.get("WWW-Authenticate")(或其他任何字段),我只会null

是否有机会使用Fetch API访问其他字段?

8 个答案:

答案 0 :(得分:50)

当您使用基于CORS的Fetch API时,访问响应标头存在限制。由于此限制,您只能访问以下标准标题:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

在为Google Chrome扩展程序编写代码时,您使用的是CORS,因此您无法访问所有标头。如果您控制服务器,则可以在回复body而非headers

中返回自定义信息

有关此限制的更多信息 - https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types

答案 1 :(得分:7)

来自MDN

您还可以通过访问Iterator条目来获取所有标题。

// Display the key/value pairs
for (var pair of res.headers.entries()) {
   console.log(pair[0]+ ': '+ pair[1]);
}

另外,请记住this部分:

  

出于安全原因,某些标头只能由用户代理控制。这些标头包括禁用的标头名称和禁止的响应标头名称。

答案 2 :(得分:3)

问题:

您可能认为这是前端问题。
这是一个后端问题。
浏览器不允许公开授权标头,除非后端告诉浏览器明确公开它。

如何解决:

这对我有用。
在后端(API)中,将此添加到响应标头中:

response.headers.add("Access-Control-Expose-Headers","Authorization")

为什么?

安全。
防止 XSS 攻击。
这个请求应该是从后端到后端。
并且后端会将 httpOnly cookie 设置到前端。
因此,您网站上的任何第三方 JS 包都不应访问授权标头。
如果您认为让前端可以访问标头是安全的,那就去做吧。
但我建议立即将服务器后端设置的 HttpOnly Cookies 发送到您的浏览器。

参考:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

答案 3 :(得分:2)

为与不支持ES2015迭代器的浏览器向后兼容(可能还需要获取/承诺polyfills), Headers.forEach 功能是最佳选择:

r.headers.forEach(function(value, name) {
    console(name + ": " + value);
});

在IE11中进行了测试,将Bluebird用作Promise polyfill,将whatwg-fetch用作fetch polyfill。 Headers.entries(),Headers.keys()和Headers.values()不起作用。

答案 4 :(得分:2)

要解决此限制问题,添加公开的标头名称就足够了。

access-control-expose-headers:headername1,headername2,...

设置此标头后,客户端脚本便能够从响应中读取那些标头(headername1,headername2,...)。

答案 5 :(得分:1)

如果没有问题:

在调试时或console.log响应时,提取不会显示标题。

您必须使用以下方式访问标头。

response.headers.get('x-auth-token')

答案 6 :(得分:0)

还请记住,如果您解析响应,则可能需要将响应传递到.then()之后的下一个res.headers.get()。我一直忘了那个...

var link
const loop = () => {
  fetch(options)
    .then(res => { 
      link = res.headers.get('link')
      return res.json()
    })
    .then(body => {
      for (let e of body.stuff) console.log(e)
      if (link) setTimeout(loop, 100)
    })
    .catch(e => {
      throw Error(e)
    })
}
loop()

答案 7 :(得分:-2)

获取标题的更简单方法是像这样使用 @extends('layouts.admin') @section('content') <div class="row"> <div class="col-md-9"> <h2 class="view-title">{{ __('Edit User') }}</h2> </div> <div class="col-md-3"> <div class="text-left"> <!-- <a class="btn btn-primary btn-admin" href="{{ route('users.index') }}"> {{ __('Back') }}</a> --> </div> </div> </div> @if ($message = Session::get('success')) <div class="alert alert-success"> <a class="close" onclick="jQuery('.alert').hide()">×</a> <p>{{ $message }}</p> </div> @endif <div class="row"> <div class="col-md-12" mt-5> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif </div> </div> {!! Form::model($user, ['method' => 'PATCH','enctype'=>'multipart/form-data','route' => ['participants.update', $user->id]]) !!} <div class="row"> <div class="col-md-3 mt-5"> <!-- @if($user->image_id != 'default-avatar.png') <button type="submit" name="resetphoto" class="btn btn-danger px-3 mr-5 pull-right"><i class="fa fa-trash" aria-hidden="true"></i> </button> @endif --> @if(empty($user->image_id)) <center> <img src="/propics/default-avatar.png" alt="Profile Pic" id="profile_pic_display_settings" class="mb-3"></center> </p> @else <center> <img src="/propics/{{$user->image_id}}" alt="Profile Pic" id="profile_pic_display_settings" class="mb-3"></center> @endif <center><label for="propic" class="btn btn-default subscribe"><i class="fas fa-camera"></i></label></center> <input id="propic" type="file" name="image_id" class="form-control"> <label id="file_name"></label> @error('propic') <span class="help-block" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <div class="col-md-9 mt-5"> <div class="form-group row"> <div class="col-md-6"> {!! Form::text('first_name', null, array('placeholder' => 'First Name','class' => 'form-control txt_txt')) !!} {!! $errors->first('first_name', '<span class="help-block" role="alert">:message</span>') !!} </div> <div class="col-md-6"> {!! Form::text('last_name', null, array('placeholder' => 'Last Name','class' => 'form-control txt_txt')) !!} {!! $errors->first('last_name', '<span class="help-block" role="alert">:message</span>') !!} </div> </div> <div class="form-group row"> <div class="col-md-6 "> {!! Form::text('email', null, array('placeholder' => 'Email','class' => 'form-control txt_txt')) !!} {!! $errors->first('email', '<span class="help-block" role="alert">:message</span>') !!} </div> <div class="col-md-6 "> {!! Form::password('password', array('placeholder' => 'Password','class' => 'form-control txt_txt')) !!} {!! $errors->first('password', '<span class="help-block" role="alert">:message</span>') !!} </div> </div> <div class="form-group row"> <div class="col-md-6 "> {!! Form::password('confirm-password', array('placeholder' => 'Confirm Password','class' => 'form-control txt_txt')) !!} </div> <div class="col-md-6"> {!! Form::select('roles[]', $roles,$userRole, array('class' => 'form-control','multiple')) !!} {!! $errors->first('roles', '<span class="help-block" role="alert">:message</span>') !!} {!! Form::text('role_id','null', array('class' => 'form-control txt_none')) !!} {!! Form::text('gender','null', array('class' => 'form-control txt_none')) !!} </div> </div> <div class="col-md-12 text-right px-0 "> <a class="btn btn-primary btn-admin-form-cancel mt-5" href="{{ route('participants.index') }}"> {{ __('Cancel') }}</a> <button type="submit" class="btn btn-primary btn-admin-form-save mt-5">{{ __('Save') }}</button> </div> </div> </div> {!! Form::close() !!} <div class="row"> <hr class="black_line"> </div> {!! Form::model($user, ['method' => 'PATCH','enctype'=>'multipart/form-data','route' => ['participants.update', $user->id]]) !!} <div class="row"> <div class="col-md-3 mt-5"> <!-- @if($user->image_id != 'default-avatar.png') <button type="submit" name="resetphoto" class="btn btn-danger px-3 mr-5 pull-right"><i class="fa fa-trash" aria-hidden="true"></i> </button> @endif --> @if(empty($user->image_id)) <center> <img src="/propics/default-avatar.png" alt="Profile Pic" id="profile_pic_display_settings" class="mb-3"></center> </p> @else <center> <img src="/propics/{{$user->image_id}}" alt="Profile Pic" id="profile_pic_display_settings" class="mb-3"></center> @endif <center><label for="propic" class="btn btn-default subscribe"><i class="fas fa-camera"></i></label></center> <input id="propic" type="file" name="image_id" class="form-control"> <label id="file_name"></label> @error('propic') <span class="help-block" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <div class="col-md-9 mt-5"> <div class="form-group row"> <div class="col-md-6"> {!! Form::text('first_name', null, array('placeholder' => 'Pet Name','class' => 'form-control txt_txt')) !!} {!! $errors->first('first_name', '<span class="help-block" role="alert">:message</span>') !!} </div> <div class="col-md-6"> {!! Form::text('pet_age', null, array('placeholder' => 'Pet Age','class' => 'form-control txt_txt')) !!} {!! $errors->first('pet_age', '<span class="help-block" role="alert">:message</span>') !!} </div> </div> <div class="col-md-12 text-right px-0 "> <a class="btn btn-primary btn-admin-form-cancel mt-5" href="{{ route('participants.index') }}"> {{ __('Cancel') }}</a> <button type="submit" class="btn btn-primary btn-admin-form-save mt-5">{{ __('Save') }}</button> </div> </div> </div> {!! Form::close() !!} @endsection

Object.fromEntries