我在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访问其他字段?
答案 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