【发布时间】:2017-07-27 01:35:04
【问题描述】:
我想知道是否有办法避免重复自己将Request.Headers 传递给每个服务方法?
[HttpGet]
[Route("accounts({id:guid})")]
[Route("accounts")]
public async Task<HttpResponseMessage> GetAccount()
{
var query = Request.RequestUri.AbsolutePath.Split('/').Last() + Request.RequestUri.Query;
var response = await _accountService.GetAccount(query, Request.Headers);
return response;
}
[HttpGet]
[Route("accounts/{id:guid}")]
public async Task<HttpResponseMessage> GetAccountByID(Guid id)
{
var query = "accounts(" + id + ")";
var response = await _accountService.GetAccount(query, Request.Headers);
return response;
}
[HttpPatch]
[Route("accounts/{id:guid}")]
public async Task<HttpResponseMessage> UpdateAccount([FromBody] JObject account, Guid id)
{
var response = await _accountService.Update(account, id, Request.Headers);
return response;
}
[HttpPost]
[Route("accounts")]
public async Task<HttpResponseMessage> CreateAccount([FromBody] JObject account)
{
return await _accountService.Create(account, Request.Headers);
}
客户端代码如下:
public async Task<HttpResponseMessage> GetAccount(string query)
{
var response = Client.Instance.GetAsync(Client.Instance.BaseAddress + query);
var responseType = response.Result.StatusCode;
if (responseType == HttpStatusCode.NotFound)
{
return new HttpResponseMessage
{
StatusCode = responseType
};
}
return await response;
}
public async Task<HttpResponseMessage> Create(JObject account)
{
var request = new HttpRequestMessage(HttpMethod.Post, Client.Instance.BaseAddress + "accounts")
{
Content = new StringContent(account.ToString(), Encoding.UTF8, "application/json")
};
var response = await Client.Instance.SendAsync(request);
var responseType = response.StatusCode;
if (responseType == HttpStatusCode.BadRequest)
{
return new HttpResponseMessage
{
StatusCode = responseType
};
}
var uri = new Uri(response.Headers.GetValues("OData-EntityId").First());
var content = await Client.Instance.GetAsync(uri);
if (content.StatusCode == HttpStatusCode.BadRequest)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest
};
}
return new HttpResponseMessage
{
Content = content.Content,
StatusCode = HttpStatusCode.NoContent == responseType ? HttpStatusCode.Created : responseType
};
}
public async Task<HttpResponseMessage> Update(JObject account, Guid id)
{
var request = new HttpRequestMessage(new HttpMethod("PATCH"), Client.Instance.BaseAddress + "accounts(" + id + ")")
{
Content = new StringContent(account.ToString(), Encoding.UTF8, "application/json")
};
var updateRequest = await Client.Instance.SendAsync(request);
var responseType = updateRequest.StatusCode;
if (responseType == HttpStatusCode.BadRequest)
{
return new HttpResponseMessage
{
StatusCode = responseType
};
}
var uri = new Uri(updateRequest.Headers.GetValues("OData-EntityId").Single());
var updateResponse = await Client.Instance.GetAsync(uri);
return updateResponse;
}
在我的重构尝试中,一个非常好的建议是合并服务层和控制器层:
[HttpGet]
[Route("accounts({id:guid})")]
[Route("accounts")]
public async Task<HttpResponseMessage> GetAccount (HttpRequestMessage Request) {
//at the line below is where i want to send the same headers that were passed in originally at step 1
var query = Request.RequestUri.AbsolutePath.Split('/').Last() + Request.RequestUri.Query;
var headers = Request.Headers;
var url = Client.Instance.BaseAddress + query;
//create new request and copy headers
var proxy = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in headers) {
proxy.Headers.Add(header.Key, header.Value);
}
var response = await Client.Instance.SendAsync(proxy);//This is an assumption.
var responseType = response.StatusCode; //Do not mix blocking calls. It can deadlock
if (responseType == HttpStatusCode.NotFound)
return new HttpResponseMessage {
StatusCode = responseType
};
return response;
}
但是,这并不能解决我对违反 DRY 的担忧。
然后我尝试了一种更实用的方法,最终可能会成功,但它可能需要更强大。它将需要处理不同的 HTTP 动词。如您所见,这些功能都是静态的。没有依赖关系,几乎没有状态突变:
public async Task<HttpResponseMessage> FunctionalGetAccount(HttpRequestMessage globalRequest)
{
var request = new HttpRequest(globalRequest);
var query = CreateQuery(request);
var url = CreateURL(query);
var proxy = CreateProxy(url);
var headers = GetHeaders(request);
AddHeaders(headers, proxy);
var response = await AwaitResponse(proxy);
var httpStatusCode = MapHttpStatusCode(response.StatusCode);
var newHttpResponse = CreateResponse(response, httpStatusCode);
return newHttpResponse;
}
private static HttpStatusCode MapHttpStatusCode(HttpStatusCode input)
{
//based on some criteria TBD
return HttpStatusCode.NotFound;
}
private static HttpResponseMessage CreateResponse(HttpResponseMessage response, HttpStatusCode newStatusCode)
{
//should be made immutable
//update the status code to newStatusCode
var updatedResponse = response;
//updatedResponse.StatusCode = newStatusCode;
//logic TBD
return updatedResponse;
}
private static async Task<HttpResponseMessage> AwaitResponse(HttpRequest proxy)
{
foreach (var header in proxy.Request.Headers)
{
Client.Instance.DefaultRequestHeaders.Add(header.Key, header.Value);
}
var response = Client.Instance.SendAsync(proxy.Request);
return await response;
}
private static void AddHeaders(HttpRequestHeaders headers, HttpRequest proxy)
{
foreach (var header in headers)
{
proxy.Request.Headers.Add(header.Key, header.Value);
}
}
private static HttpRequestHeaders GetHeaders(HttpRequest request)
{
var headers = request.Request.Headers;
return headers;
}
private static HttpRequest CreateProxy(string url)
{
var proxy = new HttpRequest(new HttpRequestMessage(HttpMethod.Get, url));
return proxy;
}
private static string CreateURL(string query)
{
var url = Client.Instance.BaseAddress + query;
return url;
}
private static string CreateQuery(HttpRequest Request)
{
var query = Request.Request.RequestUri.AbsolutePath.Split('/').Last() + Request.Request.RequestUri.Query;
return query;
}
虽然不一定是问题的核心,但这就是我定义HttpRequest的方式:
public class HttpRequest : ValueObject<HttpRequest>
{
public virtual HttpRequestMessage Request { get; }
public HttpRequest(HttpRequestMessage request)
{
Request = Cloner.CloneHttpRequestMessageAsync(request).Result;
}
protected override bool EqualsCore(HttpRequest other)
{
return other.Request.Content == Request.Content
&& other.Request.Method == Request.Method
&& other.Request.RequestUri == Request.RequestUri;
}
protected override int GetHashCodeCore()
{
return ((Request.Method.GetHashCode() * 397) ^ Request.Content.GetHashCode()) ^ Request.RequestUri.GetHashCode();
}
}
如何避免每次都必须指定将 Request.Headers 传递给每个服务方法?
附带说明一下,函数式方法的灵感主要来自 Vladimir Khorikov 和 Ralf Westphal。
【问题讨论】:
-
这可以通过依赖注入来完成,方法是在容器中与您拉取请求标头值相同的位置注册您的服务。非常类似于stackoverflow.com/questions/39459705/…
-
这可能会转向意见领域,但如果您的服务类需要访问标头,那么控制器应该每次都传递它们.我不认为这是无用的重复,而是对服务类的依赖关系非常明确。
-
服务应该不知道标头
标签: c# asp.net .net asp.net-web-api visual-studio-2017