मैं एक त्रुटि संदेश या अपवाद के साथ NotFound () IHttpActionResult कैसे लौटूँ?


98

मैं एक NotFound लौटा रहा हूं IHttpActionResult, जब मेरे WebApi GET कार्रवाई में कुछ नहीं मिला है। इस प्रतिक्रिया के साथ, मैं एक कस्टम संदेश और / या अपवाद संदेश (यदि कोई हो) भेजना चाहता हूं। वर्तमान ApiControllerकी NotFound()विधि संदेश पारित करने के लिए एक अधिभार प्रदान नहीं करता है।

ऐसा करने का कोई रास्ता नहीं है? या मुझे अपना रिवाज लिखना होगा IHttpActionResult?


क्या आप सभी नहीं मिले परिणामों के लिए एक ही संदेश वापस करना चाहते हैं?
निकोलाई सैमटेलैडज

@NikolaiSamteladze नहीं, यह स्थिति के आधार पर एक अलग संदेश हो सकता है।
अजय जाधव

जवाबों:


84

यदि आप प्रतिक्रिया संदेश आकार को अनुकूलित करना चाहते हैं तो आपको अपना स्वयं का एक्शन परिणाम लिखना होगा।

हम साधारण खाली 404s जैसी चीजों के लिए बॉक्स से बाहर सबसे आम प्रतिक्रिया संदेश आकार प्रदान करना चाहते थे, लेकिन हम इन परिणामों को भी यथासंभव सरल रखना चाहते थे; कार्रवाई के परिणामों का उपयोग करने के मुख्य लाभों में से एक यह है कि यह आपके एक्शन तरीके को इकाई परीक्षण के लिए बहुत आसान बनाता है। जितनी अधिक संपत्तियां हम कार्रवाई के परिणामों पर डालते हैं, उतनी ही चीजें आपके यूनिट परीक्षण को यह सुनिश्चित करने के लिए आवश्यक होती हैं कि कार्रवाई विधि वह कर रही है जिसकी आप अपेक्षा करते हैं।

मैं अक्सर एक कस्टम संदेश प्रदान करने की क्षमता चाहता हूं, इसलिए भविष्य में रिलीज में उस एक्शन परिणाम का समर्थन करने पर विचार करने के लिए हमारे लिए बग लॉग इन करने के लिए स्वतंत्र महसूस करें: https://aspnetwebstack.codeplex.com/workitem/list/advanced

हालांकि, कार्रवाई के परिणामों के बारे में एक अच्छी बात यह है कि यदि आप कुछ अलग करना चाहते हैं, तो आप हमेशा अपना स्वयं का आसानी से लिख सकते हैं। यहां बताया गया है कि आप इसे अपने मामले में कैसे कर सकते हैं (यह मानते हुए कि आप पाठ / सादे में त्रुटि संदेश चाहते हैं, यदि आप 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 से सीधे विरासत में मिलने के बजाय) का उपयोग किया है, तो आप "यह" भी समाप्त कर सकते हैं। हिस्सा (जो दुर्भाग्यवश विस्तार विधि को बुलाते समय आवश्यक है):

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

1
मैंने 'IHttpActionResult' के समान कार्यान्वयन को लिखा, लेकिन 'NotFound' परिणाम के लिए विशिष्ट नहीं। यह शायद ऑल 'HttpStatusCodes' के लिए काम करेगा। मेरा CustomActionResult कोड कुछ इस तरह दिखता है और मेरे कंट्रोलर का 'Get ()' एक्शन इस तरह दिखता है: 'Public IHttpActionResult Get () {रिटर्न CustomNotFoundResult ("मीज़ेज टू रिटर्न")। } 'इसके अलावा, मैंने भविष्य की रिलीज़ में इस पर विचार करने के लिए कोडप्लेक्स पर एक बग लॉग किया ।
अजय जाधव

मैं ODataControllers का उपयोग करता हूं और मुझे इसका उपयोग करना था। NotFound ("blah");
जेरथ

1
बहुत अच्छी पोस्ट, लेकिन मैं सिर्फ इनहेरिटेंस टिप के खिलाफ सिफारिश करना चाहूंगा। मेरी टीम ने बहुत समय पहले ठीक वैसा ही करने का फैसला किया था, और उसने यह करके कक्षाओं को बहुत फूला दिया। मैंने अभी हाल ही में इसे विस्तार विधियों में बदल दिया और विरासत श्रृंखला से दूर चला गया। मैं गंभीरता से लोगों को ध्यान से विचार करने की सलाह दूंगा कि उन्हें इस तरह की विरासत का उपयोग कब करना चाहिए। आमतौर पर, रचना बहुत बेहतर होती है, क्योंकि यह बहुत अधिक अपघटित होती है।
ज्यूलगॉन

6
यह कार्यक्षमता आउट-ऑफ़-द-बॉक्स होनी चाहिए। एक वैकल्पिक "रिस्पांसबॉडी" पैरामीटर सहित इकाई-परीक्षणों को प्रभावित नहीं करना चाहिए।
थियोडोर जोगोस

230

यहाँ एक साधारण संदेश के साथ IHttpActionResult NotFound को लौटाने के लिए एक-लाइनर है:

return Content(HttpStatusCode.NotFound, "Foo does not exist.");

24
लोगों को इस जवाब को वोट देना चाहिए। यह अच्छा और आसान है!
जेस

2
विदित हो कि यह समाधान HTTP हेडर स्टेटस को "404 Not Found" पर सेट नहीं करता है।
कास्पर हालवास जेन्सेन

4
@KasperHalvasJensen सर्वर से http स्थिति कोड 404 है, क्या आपको कुछ और चाहिए?
एंथोनी एफ

4
@AnthonyF आप सही हैं। मैं कंट्रोलर का उपयोग कर रहा था। कॉन्टेंट (...)। Shoud ने ApiController.Content (...) का उपयोग किया है - मेरा बुरा।
कास्पर हालवास जेन्सेन

धन्यवाद दोस्त, यह वही था जिसकी मैं तलाश कर रहा था
कप्तिन बब्बलस ६'१६ को

28

आप ResponseMessageResultचाहें तो इस्तेमाल कर सकते हैं :

var myCustomMessage = "your custom message which would be sent as a content-negotiated response"; 
return ResponseMessage(
    Request.CreateResponse(
        HttpStatusCode.NotFound, 
        myCustomMessage
    )
);

हाँ, अगर आपको बहुत कम संस्करणों की आवश्यकता है, तो मुझे लगता है कि आपको अपने कस्टम एक्शन परिणाम को लागू करने की आवश्यकता है।


मैं इस विधि के साथ गया क्योंकि यह साफ लग रहा था। मैंने सिर्फ कस्टम संदेश कहीं और इंडेंटेड रिटर्न कोड को परिभाषित किया है।
ozzy432836 16

मुझे सामग्री से यह बेहतर लगता है क्योंकि यह वास्तव में एक वस्तु देता है जिसे मैं मानक BadRequest विधि की तरह एक संदेश संपत्ति के साथ पार्स कर सकता हूं।
user1568891

7

आप HttpResponseMessage वर्ग के ReasonPhrase गुण का उपयोग कर सकते हैं

catch (Exception exception)
{
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
  {
    ReasonPhrase = exception.Message
  });
}

धन्यवाद। खैर .. यह काम करना चाहिए, लेकिन फिर मुझे हर कार्रवाई में अपने दम पर HttpResponseException का निर्माण करना होगा। कोड को कम रखने के लिए, मैं सोच रहा था कि क्या मैं किसी भी WebApi 2 सुविधाओं का उपयोग कर सकता हूं (बिल्कुल तैयार NotFount () , Ok () विधियों की तरह) और इसके लिए ReasonPhrase संदेश को पास करें।
अजय जाधव

आप अपनी खुद की एक्सटेंशन विधि NotFound (अपवाद अपवाद) बना सकते हैं, जो सही HttpResponseException को फेंक देगा
Dmytro Rudenko

@DmytroRudenko: परीक्षण के परिणाम में सुधार करने के लिए कार्रवाई के परिणाम पेश किए गए थे। HttpResponseException को यहाँ फेंकने से आप उससे समझौता करेंगे। यहां भी हमारे पास कोई अपवाद नहीं है, लेकिन ओपी एक संदेश भेजने के लिए देख रहा है।
किरण चल्ला

ठीक है, अगर आप परीक्षण के लिए NUint का उपयोग नहीं करना चाहते हैं, तो आप NotFoundResult के अपने कार्यान्वयन को लिख सकते हैं और अपने संदेश डेटा को वापस करने के लिए इसके ExecuteAsync को फिर से लिख सकते हैं। और आपके एक्शन इनवोकेशन के परिणामस्वरूप इस वर्ग का रिटर्न उदाहरण।
दमयेत्रो रुडेंको

1
ध्यान दें कि अब आप सीधे स्टेटस कोड पास कर सकते हैं, उदाहरण के लिए HttpResponseException (HttpStatusCode.NotFound)
मार्क सोउल

3

सुझाए गए 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);
    }
}

2

मैंने इसे हल किया 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);
    }
}

1

यदि आपको आधार से विरासत में मिला है NegotitatedContentResult<T>, जैसा कि उल्लेख किया गया है, और आपको अपना रूपांतरण करने की आवश्यकता नहीं है 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>सभी यह होता है देखने के लिए। इसमें बहुत कुछ नहीं है।


1

संपत्ति सेट करने के लिए मुझे IHttpActionResultएक IExceptionHandlerवर्ग के शरीर में एक उदाहरण बनाने की आवश्यकता थी ExceptionHandlerContext.Result। हालाँकि मैं एक रिवाज भी तय करना चाहता था ReasonPhrase

मैंने पाया कि एक (जो आसानी से सेट करने के लिए ReasonPhrase की अनुमति देता है) ResponseMessageResultको लपेट सकता है HttpResponseMessage

उदाहरण के लिए:

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

0

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

0

यहाँ उत्तर में थोड़ा डेवलपर कहानी समस्या याद आ रही है। ApiControllerवर्ग अभी भी एक को उजागर किया गया है NotFound()विधि कि डेवलपर्स का उपयोग कर सकते हैं। यह अनियंत्रित परिणाम निकाय को शामिल करने के लिए कुछ 404 प्रतिक्रिया का कारण होगा।

मैं यहाँ कोड के कुछ हिस्सों को " बेहतर ApiController NotFound विधि " प्रस्तुत करता हूं जो एक कम त्रुटि-प्रवण विधि प्रदान करेगा जिससे डेवलपर्स को "404 भेजने का बेहतर तरीका" जानने की आवश्यकता नहीं है।

  • बुलाया से विरासत मेंApiController एक वर्ग बनाएँApiController
    • डेवलपर्स को मूल वर्ग का उपयोग करने से रोकने के लिए मैं इस तकनीक का उपयोग करता हूं
  • NotFoundदेवों को पहले उपलब्ध एपीआई का उपयोग करने देने के लिए इसकी विधि को ओवरराइड करें
  • यदि आप इसे हतोत्साहित करना चाहते हैं, तो इसे चिह्नित करें [Obsolete("Use overload instead")]
  • एक अतिरिक्त जोड़ें protected NotFoundResult NotFound(string message)जिसे आप प्रोत्साहित करना चाहते हैं
  • समस्या: परिणाम किसी निकाय के साथ प्रतिक्रिया का समर्थन नहीं करता है। समाधान: विरासत और उपयोग NegotiatedContentResultबेहतर NotFoundResult वर्ग संलग्न देखें ।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.