Web APIコントローラーからhttpステータスコードを返す


219

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;
    }
}

ここでの問題は、これは例外ではなく、変更されていないため、クライアントキャッシュに問題がないことです。私はまた、戻り値の型がユーザーである必要があります(すべてのWeb APIの例がGETで示されているように)HttpResponseMessageまたはこのようなものを返しません。


あなたは、使用しているbetaか、夜間ビルド
Aliostad

@Aliostadベータ版を使用しています
ozba

それで、戻ることの何が問題になっていnew HttpResponseMessage(HttpStatusCode.NotModified)ますか?動かない?
Aliostad

@Aliostad戻り値の型がUserの場合、HttpResponseMessageを返すことができません。(明らかに)コンパイルされません。
ozba

回答:


251

答えがわからなかったので、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);
}

3
CreateResponseはパラメーターとしてステータスコードのみを受け取るため、ASP.NET MVC 4ベータリリースではコンパイルされません。次に、廃止予定であるため、戻り値としてHttpResponseMessageを使用しないソリューションが必要でした:aspnetwebstack.codeplex.com/discussions/350492
ozba

5
場合誰もが、コントローラメソッドから値を取得するために、それを必要とするであろうGetUser(request, id, lastModified).TryGetContentValue(out user)、ここで、user(例えば場合)であるUserオブジェクト。
Grinn

4
これはまだ2015年に推奨される方法ですか?MVC 5?
クラッシュ

4
より新しいバージョンでは、HttpResponseMessage(2017)ではなくIHttpActionResultが返されます
niico

8
niicoの提案に追加するには、戻り値の型がでIHttpActionResultあり、ユーザーを返したい場合は、を実行するだけですreturn Ok(user)。別のステータスコード(たとえば、禁止)を返す必要がある場合は、を実行できますreturn this.StatusCode(HttpStatusCode.Forbidden)
2017

68

アクションシグネチャをユーザーを返すものとして保持する場合は、次の操作も実行できます。

public User GetUser(int userId, DateTime lastModifiedAtClient) 

それ以外のものを返す場合は、アクションに200をスローし、クライアントに送信するをHttpResponseException渡しHttpResponseMessageます。


9
これは、より洗練された方法です(albitt不完全な回答)。なぜ誰もがそれを難し​​い方法で行うことを好むのですか?
nagytech 2013年

4
@Geoist stackoverflow.com/questions/1282252/...。例外のスローはコストがかかります。
tia 2013

10
ええ、多忙なAPIを設計している場合、例外を使用して最も一般的なケースを伝えるのNotModifiedは本当に無駄です。すべてのAPIがこれを行った場合、サーバーは主にワットを例外に変換します。
ルークプレット2013年

2
@nagytechは、エラー(400応答など)をスローするとカスタムエラーメッセージを返すことができないためです...また、例外をスローすることは、コードが期待することに対して愚かです。高価であり、必要に応じてログに記録したくない場合にログに記録されます。彼らは本当に例外ではありません。
Rocklan

40

MVC 5では、物事が簡単になりました。

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
メッセージを指定できませんか?
2016年

1
メッセージを使用することは、実際には受け入れられる答えです。これはほんの少し簡潔です
Jon Bates

39

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フレームワークは、オブジェクトをシリアル化して返す必要があります。

注意

一般的なバージョンは、Web APIの将来のバージョンで削除されると思います。


4
これは、私が探していた正確な答えでした-Task <HttpResponseMessage <T >>の戻り値の型ではありますが。ありがとう!
xeb 2012

1
@xeb-はい、それは完全に呼び出す価値があります。非同期の詳細については、asp.net / mvc / tutorials / mvc
/…を

14

私は古い記事にぶつかることを嫌いますが、これはグーグル検索でこれに対する最初の結果であり、私はこの問題に(あなたたちのサポートがあっても)かなりの時間を過ごしました。だからここでは何もしません...

うまくいけば、私の解決策が混乱した人たちにも役立つでしょう。

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);
}

1
鮮やかさ。時間を大幅に節約できました。
gls123

10

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

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

必要に応じて、応答でオブジェクトを返すことができます

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

7

ASP.NET Web Api 2の場合、MSからのこの投稿では、メソッドの戻り値の型をに変更することを提案していIHttpActionResultます。あなたは、その後に建て返すことができるIHttpActionResultように実装しOkBadRequest(など、こちらをご覧ください)、または独自の実装を返します。

あなたのコードでは、それは次のように行うことができます:

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);
}

3

別のオプション:

return new NotModified();

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

2
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

IHttpActionResultを返す必要があり、エラーコードとメッセージを返したい場合は、次を使用します。

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

2

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." 
                });
}

0

moremodenを使用した@Aliostads回答の更新 IHttpActionResultWeb API 2で導入され。

https://docs.microsoft.com/en-us/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

これを試して :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.