web-dev-qa-db-ja.com

エラー処理(クライアントへのex.Messageの送信)

ASP.NET Core 1.0 Web APIアプリケーションがあり、コントローラーの関数がエラーを呼び出している場合にクライアントに例外メッセージを渡す方法を見つけようとしています。

いろいろ試してみましたが、IActionResultを実装しているものはありません。

これが人々が必要とする一般的なものではない理由がわかりません。正直に解決策がない場合、誰かが私に理由を教えてもらえますか?

HttpResponseException(HttpResponseMessage)を使用したドキュメントがいくつかありますが、これを使用するには、compat shimをインストールする必要があります。 Core 1.0でこれらのことを行う新しい方法はありますか?

ここに私がシムで試しているものがありますが、うまくいきません:

// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
    Customer c = _customersService.GetCustomerById(id);
    if (c == null)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
            StatusCode = HttpStatusCode.NotFound

        };

        throw new HttpResponseException(response);

        //return NotFound();
    }
    return new ObjectResult(c);
}

HttpResponseExceptionがスローされると、クライアントを見ると、コンテンツで送信しているメッセージが見つかりません。

38
Blake Rivell

これは単純なエラーDTOクラスです

public class ErrorDto
{
    public int Code {get;set;}
    public string Message { get; set; }

    // other fields

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);
    }
}

そして、 ExceptionHandler ミドルウェアを使用します:

            app.UseExceptionHandler(errorApp =>
            {
                errorApp.Run(async context =>
                {
                    context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
                    context.Response.ContentType = "application/json";

                    var error = context.Features.Get<IExceptionHandlerFeature>();
                    if (error != null)
                    {
                        var ex = error.Error;

                        await context.Response.WriteAsync(new ErrorDto()
                        {
                            Code = <your custom code based on Exception Type>,
                            Message = ex.Message // or your custom message
                            // other custom data
                        }.ToString(), Encoding.UTF8);
                    }
                });
            });
54
Set

はい、必要に応じてステータスコードを変更できます。

CustomExceptionFilterAttribute.csファイルで、次のようにコードを変更します。

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.Result = new ContentResult
        {
            Content = $"Error: {exception.Message}",
            ContentType = "text/plain",
            // change to whatever status code you want to send out
            StatusCode = (int?)HttpStatusCode.BadRequest 
        };
    }
}

それはほとんどそれです。

カスタム例外がある場合は、コンテキストからスローされた例外を取得するときにそれらを確認することもできます。その後、コードで発生した内容に応じて異なるHTTPステータスコードを送信できます。

お役に立てば幸いです。

7
shabbirh

以下のようなカスタム例外フィルターを作成できます

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.Result = new JsonResult(exception.Message);
    }
}

次に、上記の属性をコントローラーに適用します。

[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
     // GET: api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        throw new Exception("Suckers");
        return new string[] { "value1", "value2" };
    }
}
6
cvraman

例外を発生させてキャッチするのではなく、アクションを次のように単純化してください。

// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
    var customer = _customersService.GetCustomerById(id);

    if (customer == null)
    {
        return NotFound("Customer doesn't exist");        
    }

    return Ok(customer);
}

テキストの代わりにJSONオブジェクトを返すなど、いくつかのオプションを指定して ブログ投稿 を作成しました。

2
Paul Hiles

たぶんそれは役に立ちます。 objectのみを返し、たとえばBadRequest(HTTPコード:400)を実際のパラメーターとしてカスタムobjectと共に送信できます(ここでは補間された文字列を使用しました)が、何でも。

クライアント側では、たとえば AJAXエラーハンドラー を使用して、そのエラー状況をキャッチできます。

// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{

    var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();

    var geodataItems = _context.TruckFahrerGeoData;

    foreach (var truckFahrerGeoData in geodataItems)
    {
        GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);

        if (geoTelemetryData == null)
        {
            return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
        }
        TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
        {
            Speed = geoTelemetryData.Speed,
            Accuracy = geoTelemetryData.Accuracy,
            TruckAppId = geoTelemetryData.Activity.TruckAppId,
            TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
            ClId = geoTelemetryData.Activity.ClId,
            TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
            TaskId = geoTelemetryData.Activity.TaskId,
            TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
        };

        truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
    }


    return truckFahrerGeoDataItems;
}

または、そのようなIActionResultを使用したよりクリーンな方法:

// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{

    var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();

    var geodataItems = _context.TruckFahrerGeoData;

    foreach (var truckFahrerGeoData in geodataItems)
    {
        GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);

        if (geoTelemetryData == null)
        {
            return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
        }
        TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
        {
            Speed = geoTelemetryData.Speed,
            Accuracy = geoTelemetryData.Accuracy,
            TruckAppId = geoTelemetryData.Activity.TruckAppId,
            TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
            ClId = geoTelemetryData.Activity.ClId,
            TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
            TaskId = geoTelemetryData.Activity.TaskId,
            TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
        };

        truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
    }


    return Ok(truckFahrerGeoDataItems);
}
1
Erich Brunner

私は同じ問題を抱えていましたが、いくつかの調査の後、HttpClientを使用してAPIを呼び出し、応答を簡単に読み取ることができることがわかりました。 HTTP応答にエラーコードが含まれている場合、HttpClientはエラーをスローしませんが、IsSuccessStatusCodeプロパティをfalseに設定します。

これは、HttpClientを使用する私の機能です。これをコントローラーから呼び出します。

  public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
        {
            string uri = apiUrl + url;
            using (var client = new HttpClient())
            {
                //client.BaseAddress = new Uri(uri);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
                HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));

                return response;
            }
        }

これがコントローラーコードです。関数を呼び出して応答を読み取り、エラーがあるかどうかを判断して、それに応じて応答します。 IsSuccessStatusCodeを確認していることに注意してください。

                HttpResponseMessage response;
                string url = $"Setup/AddDonor";
                var postdata = JsonConvert.SerializeObject(donor);

                response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
                //var headers = response.Headers.Concat(response.Content.Headers);
                var responseBody = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                {
                    tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));

                    return Json(new
                    {
                        ok = true,
                        message = tnxresult.Message,
                        statusCode = tnxresult.StatusCode
                    });
                }
                else
                {
                  ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));

                    return Json(new
                    {
                        ok = false,
                        message = rs.Message,
                        statusCode = rs.StatusCode
                    });

                }

APIはエラーメッセージをJSONで返します。呼び出しが成功した場合、私もJSONで応答をパックしています。

コードの重要な行はこれです...

var responseBody = await response.Content.ReadAsStringAsync();

HTTPコンテンツを非同期操作として文字列にシリアル化します。

その後、JSON文字列をオブジェクトに変換し、エラー/成功メッセージとステータスコードにもアクセスできます。

0
Onsongo Moseti