ASP.NET MVC में POST को बाँधना

विषय ASP.NET MVC अनुप्रयोगों को विकसित करने के लिए पेशेवर तकनीकों में से एक का वर्णन करता है, जो फ़ॉर्म क्रियाओं के POST संचालकों में दोहराया कोड की संख्या को काफी कम कर सकता है। इस तथ्य के बावजूद कि मैंने ASP.NET MVC इन एक्शन के पहले संस्करण और पहले mvcConf के दौरान भी इसके बारे में सीखा, जिमी बोगार्ड की प्रस्तुति की शैली मुझे बहुत सरल लगी और मैंने उन लोगों के लिए एक मुफ्त अनुवाद प्रकाशित करने का फैसला किया जो अभी भी इस दृष्टिकोण का उपयोग नहीं करते हैं ।


बहुत से लोग पूछते हैं कि AutoMapper में रिवर्स मैपिंग ( DTO s -> लगातार ऑब्जेक्ट मॉडल) के लिए इतनी कम अंतर्निहित विशेषताएं क्यों हैं। तथ्य यह है कि मौजूदा अवसर डोमेन मॉडल को बहुत सीमित करते हैं, उन्हें एनीमिक होने के लिए मजबूर करते हैं, इसलिए हमने अपने POST अनुरोधों में जटिलता से निपटने का एक और तरीका ढूंढ लिया।

मध्यम / बड़े ASP.NET MVC साइट, जटिलता या आकार को देखें, और आप देखेंगे कि कार्यान्वयन में कुछ पैटर्न दिखाई देते हैं। आपको अपने GET और POST कार्यों में जो दिखता है, उसके बीच एक बड़ा अंतर दिखाई देगा। यह अपेक्षित है क्योंकि GETs प्रश्न हैं, और POST कमांड हैं (यदि आपने उन्हें सही तरीके से लागू किया है, तो यह बिल्कुल ऐसा ही है)। आपको आवश्यक रूप से प्रपत्र टैग और POST कार्यों के लिए 1: 1 अनुपात नहीं दिखाई देगा फ़ॉर्म का उपयोग अनुरोध भेजने के लिए भी किया जा सकता है (उदाहरण के लिए, एक खोज फ़ॉर्म)।

जीईटी कार्यों के लिए, मेरी राय में, समस्या पहले ही हल हो गई है। GET क्रियाएँ एक ViewModel बनाती हैं और इसे दृश्य में भेजती हैं, और किसी भी संख्या में अनुकूलन / सार (AutoMapper, मॉडल बाइंडिंग, सम्मेलनों पर प्रक्षेपण, आदि) का उपयोग करती हैं।

POST एक पूरी तरह से अलग जानवर हैं। जानकारी बदलने और आदेश प्राप्त करने में जटिलता के क्षेत्र पूरी तरह से GETs के लिए रूढ़िवादी हैं, जो हमें अपने पिछले सभी फैसलों को दूर कर देता है। आमतौर पर हम कुछ इस तरह देखते हैं:
[HttpPost]
public ActionResult Edit(ConferenceEditModel form)
{
if (!ModelState.IsValid)
{
return View(form);
}

var conf = _repository.GetById(form.Id);

conf.ChangeName(form.Name);

foreach ( var attendeeEditModel in form.Attendees)
{
var attendee = conf.GetAttendee(attendeeEditModel.Id);

attendee.ChangeName(attendeeEditModel.FirstName, attendeeEditModel.LastName);
attendee.Email = attendeeEditModel.Email;
}

return this .RedirectToAction(c => c.Index( null ), "Default" );
}

* This source code was highlighted with Source Code Highlighter .

जो हम बार-बार देखते हैं वह बार-बार एक पैटर्न है:
[HttpPost]
public ActionResult Edit( SomeEditModel form)
{
if ( IsNotValid )
{
return ShowAView (form);
}

DoActualWork ();

return RedirectToSuccessPage ();
}


* This source code was highlighted with Source Code Highlighter .

जहां एक POST एक्शन से POST एक्शन तक लाल परिवर्तन में चिह्नित सभी चीजें हैं।

तो हमें इन कार्यों के बारे में चिंता क्यों करनी चाहिए? हम यहां जो देखते हैं, उसके आधार पर एक सामान्य निष्पादन मार्ग क्यों नहीं बनाया जाता है? यहाँ कुछ कारण दिए गए हैं जिनका हम सामना कर रहे हैं:

वर्कअराउंड के रूप में, हमने तकनीकों के संयोजन का उपयोग किया:

हम हमेशा इन अमूर्तियों को बनाना नहीं चाहते हैं, लेकिन यह POST की जटिलता के प्रबंधन के लिए उपयोगी हो सकता है। आरंभ करने के लिए, एक क्रिया परिणाम बनाते हैं।

निष्पादन के समग्र प्रवाह का निर्धारण


कार्रवाई का परिणाम तैयार करने के मार्ग पर बहुत दूर जाने से पहले, आइए ऊपर दिए गए सामान्य टेम्पलेट को देखें। नियंत्रक क्रिया में कुछ चीजों को परिभाषित किया जाना चाहिए, लेकिन अन्य यादृच्छिक हो सकते हैं। उदाहरण के लिए, "DoActualWork" ब्लॉक प्राप्त फॉर्म के आधार पर निर्धारित किया जा सकता है। फ़ॉर्म की कार्रवाई को संभालने के लिए हमारे पास कभी भी 2 अलग-अलग तरीके नहीं होंगे, इसलिए आइए इस फ़ॉर्म को संभालने के लिए एक इंटरफ़ेस निर्धारित करें:
public interface IFormHandler<T>
{
void Handle(T form);
}

* This source code was highlighted with Source Code Highlighter .

यह काफी सरल है, एक वर्ग जो " एक्शन (टी) " या कमांड पैटर्न के कार्यान्वयन का प्रतिनिधित्व करता है। वास्तव में, यदि आप संदेशों से परिचित हैं, तो यह एक संदेश हैंडलर की तरह दिखता है। एक फॉर्म एक संदेश है और हैंडलर जानता है कि इस तरह के संदेश का क्या करना है।

उपरोक्त अमूर्तता यह दर्शाती है कि हमें DoActualWork ब्लॉक के लिए क्या करने की आवश्यकता है, और बाकी को कार्रवाई के समग्र परिणाम में खींचा जा सकता है:
public class FormActionResult<T> : ActionResult
{
public ViewResult Failure { get ; private set ; }
public ActionResult Success { get ; private set ; }
public T Form { get ; private set ; }

public FormActionResult(T form, ActionResult success, ViewResult failure)
{
Form = form;
Success = success;
Failure = failure;
}

public override void ExecuteResult(ControllerContext context)
{
if (!context.Controller.ViewData.ModelState.IsValid)
{
Failure.ExecuteResult(context);

return ;
}

var handler = ObjectFactory.GetInstance<IFormHandler<T>>();

handler.Handle(Form);

Success.ExecuteResult(context);
}
}

* This source code was highlighted with Source Code Highlighter .

हमने मुख्य निष्पादन पाइपलाइन की जांच की, और ऐसे टुकड़े पाए जो बदलते हैं। यह उल्लेखनीय है कि ये एक सफल परिणाम के मामले में निष्पादन के लिए ActionResult हैं और असफल परिणाम के मामले में ActionResult निष्पादन के लिए हैं। निष्पादन के लिए विशिष्ट फॉर्म हैंडलर पहले से ही रूपों के प्रकार के आधार पर परिभाषित किया गया है, इसलिए हम निष्पादन के लिए विशिष्ट फॉर्म हैंडलर खोजने के लिए किसी भी लोकप्रिय IoC कंटेनर का उपयोग करते हैं (मेरे मामले में StructureMap )। आइए स्ट्रक्चोरपॉइंट को IFormHandler कार्यान्वयन के आधार पर कार्यान्वयन खोजने के लिए कहें, यह कोड की केवल एक पंक्ति है:
Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.ConnectImplementationsToTypesClosing( typeof (IFormHandler<>));
});

* This source code was highlighted with Source Code Highlighter .

अब "DoActualWork" ब्लॉक को कक्षा में खींचें, जो केवल फ़ॉर्म को संसाधित करने से संबंधित है, और ट्रैफ़िक UI को ट्रैक नहीं कर रहा है:
public class ConferenceEditModelFormHandler
: IFormHandler<ConferenceEditModel>
{
private readonly IConferenceRepository _repository;

public ConferenceEditModelFormHandler(
IConferenceRepository repository)
{
_repository = repository;
}

public void Handle(ConferenceEditModel form)
{
Conference conf = _repository.GetById(form.Id);

conf.ChangeName(form.Name);

foreach ( var attendeeEditModel in GetAttendeeForms(form))
{
Attendee attendee = conf.GetAttendee(attendeeEditModel.Id);

attendee.ChangeName(attendeeEditModel.FirstName,
attendeeEditModel.LastName);
attendee.Email = attendeeEditModel.Email;
}
}

private ConferenceEditModel.AttendeeEditModel[] GetAttendeeForms(ConferenceEditModel form)
{
return form.Attendees ??
new ConferenceEditModel.AttendeeEditModel[0];
}
}

* This source code was highlighted with Source Code Highlighter .

अब यह वर्ग केवल सफल फॉर्म प्रसंस्करण के लिए बनाया गया है। अर्थात्, मेरे डोमेन ऑब्जेक्ट पर लौट रहा है और तदनुसार इसे बदल रहा है। क्योंकि मेरे पास एक व्यवहार डोमेन मॉडल है, आप "रिवर्स मैपिंग" की संभावना नहीं देखेंगे। यह उद्देश्य पर किया जाता है।

यह वास्तव में दिलचस्प है कि हम इन सभी समस्याओं को कैसे अलग करते हैं और यह कि वे विशिष्ट ASP.NET कार्रवाई परिणाम पर निर्भर नहीं करते हैं। फिलहाल, हमने काम करने की समस्याओं को प्रत्यक्ष काम से अलग कर दिया है।

हमारे नियंत्रक के लिए लागू


अब जब हमने अपना क्रिया परिणाम विकसित कर लिया है, तो अंतिम समस्या बनी हुई है - इस क्रिया के परिणाम को हमारे नियंत्रक क्रिया पर लागू करना। ज्यादातर लोगों की तरह, हम अक्सर नियंत्रक कक्षाओं की पदानुक्रम में एक परत डालते हैं ताकि हमारे सभी नियंत्रकों में सहायक विधियों का उपयोग किया जा सके। इस कक्षा में, हम अपने कस्टम एक्शन परिणाम बनाने के लिए एक सहायक विधि जोड़ेंगे:
public class DefaultController : Controller
{
protected FormActionResult<TForm> Form<TForm>(
TForm form,
ActionResult success)
{
var failure = View(form);

return new FormActionResult<TForm>(form, success, failure);
}

* This source code was highlighted with Source Code Highlighter .

यह बस कुछ डिफ़ॉल्ट रास्तों को लपेटता है जिन्हें हम अक्सर परिभाषित करते हैं। उदाहरण के लिए, एक प्रसंस्करण त्रुटि लगभग हमेशा वह दृश्य दिखाती है जिससे हम अभी आए थे। अंत में, हम अपने मूल POST नियंत्रक कार्रवाई को बदल सकते हैं:
public class ConferenceController : DefaultController
{
[HttpPost]
public ActionResult Edit(ConferenceEditModel form)
{
var successResult =
this .RedirectToAction(c => c.Index( null ), "Default" );

return Form(form, successResult);
}

* This source code was highlighted with Source Code Highlighter .

हमने नियंत्रक की कार्रवाई को कम कर दिया है ताकि यह वास्तव में यह वर्णन कर सके कि हम क्या कर रहे हैं, और जिस तरह से हम इसे करते हैं, हम निचले स्तर पर चले गए हैं। यह व्यवहार में OO संरचना के अनुप्रयोग का एक उत्कृष्ट उदाहरण है, हमने कार्रवाई के परिणाम में POST फॉर्म को निष्पादित करने के लिए विभिन्न तरीकों को संयुक्त किया है और फॉर्म हैंडलर को लागू किया है। वास्तव में, हमने उस कोड को कम नहीं किया जिसे हम लिखने के लिए मजबूर हैं, यह बस चला गया, और यह हमारे लिए तर्क के लिए थोड़ा आसान हो गया।

एक और दिलचस्प दुष्प्रभाव यह है कि अब हम फॉर्म हैंडलर के लिए इकाई / एकीकरण परीक्षण बना रहे हैं, लेकिन नियंत्रक कार्रवाई के लिए नहीं। और जांच करने के लिए क्या है? हमारे पास परीक्षण लिखने के लिए कोई प्रोत्साहन नहीं है, जैसा कि बहुत कम तर्क है।

बड़े पैमाने के पैटर्न के उपयोग को देखते समय, मुख्य रूप से मार्गों के संघ की जांच करना महत्वपूर्ण है। यह हमें विरासत वाले मार्गों की तुलना में भागों को एक साथ रखने में थोड़ा अधिक लचीला बनाता है।

हालाँकि यह एक कुछ जटिल उदाहरण है, अगले लेख में हम देखेंगे कि जब हमारी मान्यता सरल तत्वों से आगे बढ़ जाती है तो POST कार्रवाई कितनी जटिल हो सकती है और हमारे POST संचालक और भी जटिल हो जाते हैं।

Source: https://habr.com/ru/post/In123113/


All Articles