web-dev-qa-db-ja.com

Web Apiコントロールロールからhttpステアコードを返す

私はWeb APIコントローラのGETメソッド用に変更されていない304のステータスコードを返そうとしています。

私が成功した唯一の方法は、次のようなものでした。

public class TryController : ApiController
{
    public User GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
             throw new HttpResponseException(HttpStatusCode.NotModified);
        }
        return user;
    }
}

ここでの問題はそれが例外ではないということです、それは単に修正されないのでクライアントキャッシュはOKです。また、戻り値の型がUserであり(すべてのWeb APIの例がGETで示されているように)HttpResponseMessageなどを返さないようにする必要があります。

201
ozba

私は答えを知りませんでしたので、ここでASP.NETチーム に尋ねました

そのためのコツは、シグネチャをHttpResponseMessageに変更してRequest.CreateResponseを使用することです。

[ResponseType(typeof(User))]
public HttpResponseMessage GetUser(HttpRequestMessage request, int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
         return new HttpResponseMessage(HttpStatusCode.NotModified);
    }
    return request.CreateResponse(HttpStatusCode.OK, user);
}
232
Aliostad

Userを返すときにアクションシグネチャを保持したい場合は、次のこともできます。

public User GetUser(int userId, DateTime lastModifiedAtClient) 

200以外のものを返したい場合は、アクションにHttpResponseExceptionを投入し、クライアントに送信したいHttpResponseMessageを渡します。

GetXxx APIメソッドをHttpResponseMessageを返すように変更してから、フルレスポンスの型付きバージョンとNotModifiedレスポンスの型なしバージョンを返します。

    public HttpResponseMessage GetComputingDevice(string id)
    {
        ComputingDevice computingDevice =
            _db.Devices.OfType<ComputingDevice>()
                .SingleOrDefault(c => c.AssetId == id);

        if (computingDevice == null)
        {
            return this.Request.CreateResponse(HttpStatusCode.NotFound);
        }

        if (this.Request.ClientHasStaleData(computingDevice.ModifiedDate))
        {
            return this.Request.CreateResponse<ComputingDevice>(
                HttpStatusCode.OK, computingDevice);
        }
        else
        {
            return this.Request.CreateResponse(HttpStatusCode.NotModified);
        }
    }

* ClientHasStaleデータは、ETagおよびIfModifiedSinceヘッダーをチェックするための私の拡張です。

MVCフレームワークはまだあなたのオブジェクトをシリアル化して返すべきです。

NOTE

ジェネリック版は将来のバージョンのWeb APIで削除されると思います。

39
Luke Puplett

MVC 5では、物事はより簡単になりました:

return new StatusCodeResult(HttpStatusCode.NotModified, this);
37
Jon Bates

私は古い記事をぶつけて嫌いですが、これはグーグル検索でこれのための最初の結果であり、私はこの問題に多少の時間がありました(あなたたちのサポートでさえ)。だからここでは何もしない...

うまくいけば私の解決策はまた混乱していたものを助けるでしょう。

namespace MyApplication.WebAPI.Controllers
{
    public class BaseController : ApiController
    {
        public T SendResponse<T>(T response, HttpStatusCode statusCode = HttpStatusCode.OK)
        {
            if (statusCode != HttpStatusCode.OK)
            {
                // leave it up to Microsoft to make this way more complicated than it needs to be
                // seriously i used to be able to just set the status and leave it at that but nooo... now 
                // i need to throw an exception 
                var badResponse =
                    new HttpResponseMessage(statusCode)
                    {
                        Content =  new StringContent(JsonConvert.SerializeObject(response), Encoding.UTF8, "application/json")
                    };

                throw new HttpResponseException(badResponse);
            }
            return response;
        }
    }
}

そしてBaseControllerから継承する

[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...

そしてそれを使って

[HttpGet]
[Route("device/search/{property}/{value}")]
public SearchForDeviceResponse SearchForDevice(string property, string value)
{
    //todo: limit search property here?
    var response = new SearchForDeviceResponse();

    var results = _deviceManagementBusiness.SearchForDevices(property, value);

    response.Success = true;
    response.Data = results;

    var statusCode = results == null || !results.Any() ? HttpStatusCode.NoContent : HttpStatusCode.OK;

    return SendResponse(response, statusCode);
}
11
Kenneth Garza

.netコア2.2が304ステータスコードを返しています。これはApiControllerを使用しています。

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304);
    }

必要に応じて、レスポンスと共にオブジェクトを返すことができます

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }
5
Ives.me
public HttpResponseMessage Post(Article article)
{
    HttpResponseMessage response = Request.CreateResponse<Article>(HttpStatusCode.Created, article);

    string uriToTheCreatedItem = Url.Route(null, new { id = article.Id });
    response.Headers.Location = new Uri(Request.RequestUri, uriToTheCreatedItem);

    return response;
}
2
Jo Smo

ASP.NET Web Api 2の場合、 このMSからの投稿 では、メソッドの戻り値の型をIHttpActionResultに変更することをお勧めします。 IHttpActionResultOkなどのような組み込みのBadRequest実装を返すこともできますし、( こちら を参照)または独自の実装を返すこともできます。

あなたのコードでは、次のようになります。

public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
        return StatusCode(HttpStatusCode.NotModified);
    }
    return Ok(user);
}
2
datchung

IHttpActionResultを返し、エラーコードとメッセージを返したい場合は、次のようにします。

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
2
Chris Halcrow

別のオプション:

return new NotModified();

public class NotModified : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotModified);
        return Task.FromResult(response);
    }
}
1
Bora Aydın

HttpCreateResponseタイプを使用するように署名を変更する必要がないので、それを隠すための拡張ソリューションを少し思いついた。

public class HttpActionResult : IHttpActionResult
{
    public HttpActionResult(HttpRequestMessage request) : this(request, HttpStatusCode.OK)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code) : this(request, code, null)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code, object result)
    {
        Request = request;
        Code = code;
        Result = result;
    }

    public HttpRequestMessage Request { get; }
    public HttpStatusCode Code { get; }
    public object Result { get; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Request.CreateResponse(Code, Result));
    }
}

それから、あなたのApiController(あるいはもっと良いあなたのベースコントローラ)に次のようなメソッドを追加することができます。

protected IHttpActionResult CustomResult(HttpStatusCode code, object data) 
{
    // Request here is the property on the controller.
    return new HttpActionResult(Request, code, data);
}

それから、あなたはそれを組み込みメソッドのいずれかと同じように返すことができます。

[HttpPost]
public IHttpActionResult Post(Model model)
{
    return model.Id == 1 ?
                Ok() :
                CustomResult(HttpStatusCode.NotAcceptable, new { 
                    data = model, 
                    error = "The ID needs to be 1." 
                });
}
1
krillgar

@Aliostadsの更新は、Web API 2で導入されたより現代的なIHttpActionResultを使用して回答します。

https://docs.Microsoft.com/ja-jp/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results#ihttpactionresult

public class TryController : ApiController
{
    public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
            return StatusCode(HttpStatusCode.NotModified);
            // If you would like to return a Http Status code with any object instead:
            // return Content(HttpStatusCode.InternalServerError, "My Message");
        }
        return Ok(user);
    }
}
0
Ogglas