IHttpActionResult
WebApi GETアクションで何かが見つからない場合、NotFoundを返します。この応答とともに、カスタムメッセージや例外メッセージ(ある場合)を送信します。current ApiController
のNotFound()
メソッドは、メッセージを渡すためのオーバーロードを提供しません。
これを行う方法はありますか?または私は自分のカスタムを作成する必要がありIHttpActionResult
ますか?
IHttpActionResult
WebApi GETアクションで何かが見つからない場合、NotFoundを返します。この応答とともに、カスタムメッセージや例外メッセージ(ある場合)を送信します。current ApiController
のNotFound()
メソッドは、メッセージを渡すためのオーバーロードを提供しません。
これを行う方法はありますか?または私は自分のカスタムを作成する必要がありIHttpActionResult
ますか?
回答:
応答メッセージの形状をカスタマイズする場合は、独自のアクション結果を作成する必要があります。
単純な空の404などの最も一般的な応答メッセージの形状をそのまま提供したかったのですが、これらの結果をできるだけ単純にしたかったのです。アクション結果を使用する主な利点の1つは、アクションメソッドの単体テストがはるかに簡単になることです。アクションの結果に設定するプロパティが多いほど、アクションメソッドが期待どおりの動作をしていることを確認するために、単体テストで考慮する必要のある事項が多くなります。
私はしばしばカスタムメッセージも提供できるようにしたいので、バグをログに記録して、将来のリリースでそのアクションの結果をサポートすることを検討してください:https : //aspnetwebstack.codeplex.com/workitem/list/advanced
ただし、アクションの結果の良い点の1つは、少し異なることをしたい場合は、常にかなり簡単に独自のアクションを作成できることです。あなたのケースでこれを行う方法は次のとおりです(text / plainにエラーメッセージが必要だと仮定します。JSONが必要な場合は、コンテンツで少し異なることを行います)。
public class NotFoundTextPlainActionResult : IHttpActionResult
{
public NotFoundTextPlainActionResult(string message, HttpRequestMessage request)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
if (request == null)
{
throw new ArgumentNullException("request");
}
Message = message;
Request = request;
}
public string Message { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound);
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
response.RequestMessage = Request;
return response;
}
}
public static class ApiControllerExtensions
{
public static NotFoundTextPlainActionResult NotFound(this ApiController controller, string message)
{
return new NotFoundTextPlainActionResult(message, controller.Request);
}
}
次に、アクションメソッドで、次のようにすることができます。
public class TestController : ApiController
{
public IHttpActionResult Get()
{
return this.NotFound("These are not the droids you're looking for.");
}
}
(ApiControllerから直接継承する代わりに)カスタムコントローラーの基本クラスを使用した場合は、「this」を削除することもできます。パート(これは、残念ながら拡張メソッドを呼び出すときに必要です):
public class CustomApiController : ApiController
{
protected NotFoundTextPlainActionResult NotFound(string message)
{
return new NotFoundTextPlainActionResult(message, Request);
}
}
public class TestController : CustomApiController
{
public IHttpActionResult Get()
{
return NotFound("These are not the droids you're looking for.");
}
}
簡単なメッセージでIHttpActionResult NotFoundを返すためのワンライナーは次のとおりです。
return Content(HttpStatusCode.NotFound, "Foo does not exist.");
あなたがResponseMessageResult
好きなら使うことができます:
var myCustomMessage = "your custom message which would be sent as a content-negotiated response";
return ResponseMessage(
Request.CreateResponse(
HttpStatusCode.NotFound,
myCustomMessage
)
);
そうです、もっと短いバージョンが必要な場合は、カスタムアクションの結果を実装する必要があると思います。
HttpResponseMessageクラスのReasonPhraseプロパティを使用できます
catch (Exception exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
{
ReasonPhrase = exception.Message
});
}
d3m3t3erが提案するように、カスタムのネゴシエートされたコンテンツ結果を作成できます。しかし、私はから継承します。また、NotFoundを返すためだけに必要な場合は、コンストラクターからhttpステータスを初期化する必要はありません。
public class NotFoundNegotiatedContentResult<T> : NegotiatedContentResult<T>
{
public NotFoundNegotiatedContentResult(T content, ApiController controller)
: base(HttpStatusCode.NotFound, content, controller)
{
}
public override Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return base.ExecuteAsync(cancellationToken).ContinueWith(
task => task.Result, cancellationToken);
}
}
OkNegotiatedContentResult
結果の応答メッセージのHTTPコードから単純に派生させてオーバーライドすることで解決しました。このクラスを使用すると、任意のHTTP応答コードでコンテンツ本文を返すことができます。
public class CustomNegotiatedContentResult<T> : OkNegotiatedContentResult<T>
{
public HttpStatusCode HttpStatusCode;
public CustomNegotiatedContentResult(
HttpStatusCode httpStatusCode, T content, ApiController controller)
: base(content, controller)
{
HttpStatusCode = httpStatusCode;
}
public override Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return base.ExecuteAsync(cancellationToken).ContinueWith(
task => {
// override OK HTTP status code with our own
task.Result.StatusCode = HttpStatusCode;
return task.Result;
},
cancellationToken);
}
}
NegotitatedContentResult<T>
前述のようにbaseから継承し、変換する必要がない場合content
(たとえば、文字列を返したいだけの場合)、ExecuteAsync
メソッドをオーバーライドする必要はありません。
必要なのは、適切な型定義と、どのHTTPステータスコードを返すかをベースに伝えるコンストラクタを提供することだけです。それ以外はすべて機能します。
ここでは、両方のための例ですNotFound
とInternalServerError
:
public class NotFoundNegotiatedContentResult : NegotiatedContentResult<string>
{
public NotFoundNegotiatedContentResult(string content, ApiController controller)
: base(HttpStatusCode.NotFound, content, controller) { }
}
public class InternalServerErrorNegotiatedContentResult : NegotiatedContentResult<string>
{
public InternalServerErrorNegotiatedContentResult(string content, ApiController controller)
: base(HttpStatusCode.InternalServerError, content, controller) { }
}
そして、対応する拡張メソッドを作成できますApiController
(または、基本クラスがある場合はそれを行います)。
public static NotFoundNegotiatedContentResult NotFound(this ApiController controller, string message)
{
return new NotFoundNegotiatedContentResult(message, controller);
}
public static InternalServerErrorNegotiatedContentResult InternalServerError(this ApiController controller, string message)
{
return new InternalServerErrorNegotiatedContentResult(message, controller);
}
そして、それらは組み込みメソッドと同じように機能します。既存のものを呼び出すかNotFound()
、新しいカスタムを呼び出すことができますNotFound(myErrorMessage)
。
そしてもちろん、カスタムタイプ定義で「ハードコードされた」文字列タイプを削除して、必要に応じてそれをジェネリックのままにしておくこともできますが、その場合はExecuteAsync
、<T>
実際のがあります。
のソースコードを調べて、NegotiatedContentResult<T>
すべての機能を確認できます。それにはあまりありません。
プロパティを設定するためにIHttpActionResult
、IExceptionHandler
クラスの本体にインスタンスを作成する必要がありましたExceptionHandlerContext.Result
。しかし、私もカスタムを設定したかったReasonPhrase
。
をResponseMessageResult
ラップできることがわかりましたHttpResponseMessage
(これにより、ReasonPhraseを簡単に設定できます)。
例えば:
public class MyExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var ex = context.Exception as IRecordNotFoundException;
if (ex != null)
{
context.Result = new ResponseMessageResult(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = $"{ex.EntityName} not found" });
}
}
}
Iknow POがメッセージテキストで尋ねたが、404を返すだけの別のオプションは、メソッドがIHttpActionResultを返し、StatusCode関数を使用することです
public async Task<IHttpActionResult> Get([FromUri]string id)
{
var item = await _service.GetItem(id);
if(item == null)
{
StatusCode(HttpStatusCode.NotFound);
}
return Ok(item);
}
ここでの回答には、開発者ストーリーの小さな問題が欠けています。ApiController
クラスはまだAを公開していますNotFound()
開発者が使用してよい方法を。これにより、一部の404応答に、制御されていない結果本体が含まれることになります。
ここでは、開発者が「404を送信するためのより良い方法」を知る必要がない、エラーが発生しにくいメソッドを提供するコードのいくつかの部分「より良いApiController NotFoundメソッド」を示します。
ApiController
するクラスを作成するApiController
NotFound
メソッドをオーバーライドして、開発者が最初に利用可能なAPIを使用できるようにします[Obsolete("Use overload instead")]
protected NotFoundResult NotFound(string message)
奨励したいをしますNegotiatedContentResult
。付属のNotFoundResultクラスを参照してください。