Is this why it's not binding?
The reason is that your action method expects a accessToken from request headers :
public async Task<ActionResult> GetAccountAsync([FromHeader] string accessToken)
While you there's no such a AccessToken: xxx_yyy_zzz header in the request .
If you send a request as below :
GET https://localhost:44323/api/values/account HTTP/1.1
accessToken : xxx_yyy_zzz
The ModelBinder will bind the accessToken.
If so, how do I pass it correctly?
I'm not sure why you want to get the accessToken within an action method . However , if you do need the access token by model binding , there're at least two ways to do that :
One way to do that is to change your action method to get the Authorization header directly:
public async Task<ActionResult> GetAccount2Async([FromHeader] string authorization)
{
if (String.IsNullOrEmpty(authorization)) { /* */ }
if (!authorization.StartsWith("accessToken",StringComparison.OrdinalIgnoreCase)) { /* */ }
var token = authorization.Substring("accessToken".Length).Trim();
// ...
}
It will works when you send a request with a header of Authorization : accessToken xxx_yyy_zzz .
However , the approach above is not nice and clean . A better way is to create a custom ModelBinder .
Firstly Let's create a dummy class to hold the accessToken value :
public class AccessTokenAuthorizationHeader
{
public string TokenValue { get; set; }
}
And here's a simple model binder that will retrieve access token from headers :
public class AuthorizationHeaderBinder : IModelBinder
{
const string DEFAULT_ACCESS_TOKEN_AUTH_HEADER_PREFIX = "accessToken";
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
var modelName = bindingContext.BinderModelName;
if (string.IsNullOrEmpty(modelName)) { modelName = DEFAULT_ACCESS_TOKEN_AUTH_HEADER_PREFIX; }
var authorization = bindingContext.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
if (String.IsNullOrWhiteSpace(authorization)) {
return Task.CompletedTask;
}
if (!authorization.StartsWith(modelName, StringComparison.OrdinalIgnoreCase)) {
return Task.CompletedTask;
}
var token = authorization.Substring(modelName.Length).Trim();
bindingContext.Result = ModelBindingResult.Success(new AccessTokenAuthorizationHeader() {
TokenValue =token,
});
return Task.CompletedTask;
}
}
Lastly , decorate the previous AccessTokenAuthorizationHeader with a ModelBinderAttribute:
[ModelBinder(BinderType =typeof(AuthorizationHeaderBinder))]
public class AccessTokenAuthorizationHeader
{
public string TokenValue { get; set; }
}
And now we can bind it automatically :
[HttpGet("Account3")]
public async Task<ActionResult> GetAccount3Async(AccessTokenAuthorizationHeader accessToken) {
var result =new JsonResult(accessToken?.TokenValue);
return result;
}
Let's test it with a requset :
GET https://localhost:44323/api/values/account3 HTTP/1.1
Authorization : accessToken 111111
The response will be :
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTEzXFNPLkF1dGhvcml6YXRpb25IZWFkZXJcQXBwXEFwcFxhcGlcdmFsdWVzXGFjY291bnQz?=
X-Powered-By: ASP.NET
Date: Thu, 13 Sep 2018 01:54:25 GMT
"111111"
[FromHeader]attribute know where to get accessToken from?"accessToken": "theGUID"- The whole header is actuallyAuthorization: accessToken theGUID. The:demarcates the header/value pair. As @CamiloTerevinto states, you can change the parameter toauthorization, which will give you a value ofaccessToken theGUID.