এএসপি.এনইটি কোর-এ আইপি-মূলসূত্র উপহাস করা হচ্ছে


91

আমার কাছে একটি এএসপি.নেট এমভিসি কোর অ্যাপ্লিকেশন রয়েছে যার জন্য আমি ইউনিট পরীক্ষা লিখছি। ক্রিয়াকলাপগুলির মধ্যে একটিতে কিছু কার্যকারিতার জন্য ব্যবহারকারীর নাম ব্যবহার করা হয়:

SettingsViewModel svm = _context.MySettings(User.Identity.Name);

যা ইউনিট পরীক্ষায় স্পষ্টতই ব্যর্থ হয়। আমি চারপাশে দেখেছি এবং HTTPContext উপহাস করার জন্য .NET 4.5 থেকে সমস্ত পরামর্শ রয়েছে। আমি নিশ্চিত যে এটি করার আরও ভাল উপায় আছে। আমি আইপিরিসিনাল ইনজেকশনের চেষ্টা করেছি, তবে এটি একটি ত্রুটি ছুঁড়েছে; এবং আমি এটি চেষ্টাও করেছি (হতাশার বাইরে, আমি মনে করি):

public IActionResult Index(IPrincipal principal = null) {
    IPrincipal user = principal ?? User;
    SettingsViewModel svm = _context.MySettings(user.Identity.Name);
    return View(svm);
}

তবে এটি ত্রুটিও ছুঁড়েছে। দস্তাবেজগুলিতেও কিছু পাওয়া যায়নি ...

উত্তর:


182

নিয়ামক এর অ্যাক্সেস করা হয় মাধ্যমে নিয়ন্ত্রক । আধুনিক সংরক্ষণ করা হয় মধ্যে ।User HttpContextControllerContext

ব্যবহারকারীর সেট করার সহজতম উপায় হ'ল নির্ধারিত ব্যবহারকারীর সাথে একটি ভিন্ন এইচটিপিপি কনটেক্সট বরাদ্দ করা। আমরা DefaultHttpContextএই উদ্দেশ্যে ব্যবহার করতে পারি , সেভাবে আমাদের সবকিছু উপহাস করার দরকার নেই। তারপরে আমরা কেবল একটি নিয়ামক প্রসঙ্গে সেই HttpContext ব্যবহার করি এবং এটি নিয়ামকের উদাহরণে প্রেরণ করি:

var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
    new Claim(ClaimTypes.Name, "example name"),
    new Claim(ClaimTypes.NameIdentifier, "1"),
    new Claim("custom-claim", "example claim value"),
}, "mock"));

var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
    HttpContext = new DefaultHttpContext() { User = user }
};

আপনার নিজের তৈরি করার সময়, কনস্ট্রাক্টরের কাছে ClaimsIdentityএকটি স্পষ্টভাবে পাস করার বিষয়টি নিশ্চিত করুন authenticationType। এটি নিশ্চিত করে যে এটি IsAuthenticatedসঠিকভাবে কাজ করবে (আপনি যদি কোনও ব্যবহারকারী প্রমাণীকৃত কিনা তা নির্ধারণের জন্য আপনার কোডটিতে এটি ব্যবহার করেন)।


7
আমার ক্ষেত্রে এটি new Claim(ClaimTypes.Name, "1")নিয়ামক ব্যবহারের সাথে মেলে user.Identity.Name; তবে অন্যথায় এটি হ'ল আমি অর্জন করার চেষ্টা করছিলাম ... ড্যাঙ্ক শ্যাওন!
ফেলিক্স

অগণিত ঘন্টা অনুসন্ধানের পরে এই পোস্টটি ছিল যা আমাকে অবশেষে দূরে সরিয়ে দেয়। আমার মূল 2.0 প্রোজেক্ট কন্ট্রোলার পদ্ধতিতে আমি User.FindFirstValue(ClaimTypes.NameIdentifier);যে আইটেমটি তৈরি করছি তাতে ইউজারআইডি সেট করতে ব্যবহার করছিলাম এবং অধ্যক্ষটি নালার কারণে ব্যর্থ হচ্ছিল। এটা আমার জন্য এটি স্থির। মহান উত্তরের জন্য ধন্যবাদ!
টিমোথি র্যান্ডাল

আমি ইউজারম্যানেজ.গেট ইউজারএন্সিঙ্ককে কাজ করতে পেতেও অগণিত ঘন্টা অনুসন্ধান করছিলাম এবং এটিই আমি খুঁজে পেয়েছি যে আমি খুঁজে পাওয়া যায়নি link ধন্যবাদ! জেনেরিক আইডেন্টিটি ব্যবহার না করে একটি দাবি সম্বলিত একটি দাবি আইডেন্টিটি সেটআপ করা দরকার।
এটিয়েন চারল্যান্ড

17

পূর্ববর্তী সংস্করণগুলিতে আপনি Userসরাসরি কন্ট্রোলারে সেট করতে পারতেন যা কিছু খুব সহজ ইউনিট পরীক্ষার জন্য তৈরি করেছিল।

আপনি যদি কন্ট্রোলারবেস এর উত্স কোডটি লক্ষ্য করেন Userতবে লক্ষ্য করবেন যে এখান থেকে উত্তোলন করা হয়েছে HttpContext

/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;

এবং নিয়ামকটি HttpContextমাধ্যমে অ্যাক্সেস করেControllerContext

/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;

আপনি লক্ষ্য করবেন যে এই দু'টি কেবল পঠিত সম্পত্তি। সুসংবাদটি হ'ল ControllerContextসম্পত্তি এটির মান নির্ধারণের অনুমতি দেয় যাতে এটি আপনার প্রবেশ পথে চলে।

সুতরাং লক্ষ্য যে অবজেক্টে পেতে। কোর ইন HttpContextবিমূর্ত হয় তাই এটি উপহাস করা খুব সহজ।

একটি নিয়ামক মত অনুমান করা

public class MyController : Controller {
    IMyContext _context;

    public MyController(IMyContext context) {
        _context = context;
    }

    public IActionResult Index() {
        SettingsViewModel svm = _context.MySettings(User.Identity.Name);
        return View(svm);
    }

    //...other code removed for brevity 
}

মোক ব্যবহার করে, একটি পরীক্ষা এর মতো দেখতে পারে

public void Given_User_Index_Should_Return_ViewResult_With_Model() {
    //Arrange 
    var username = "FakeUserName";
    var identity = new GenericIdentity(username, "");

    var mockPrincipal = new Mock<ClaimsPrincipal>();
    mockPrincipal.Setup(x => x.Identity).Returns(identity);
    mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);

    var mockHttpContext = new Mock<HttpContext>();
    mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);

    var model = new SettingsViewModel() {
        //...other code removed for brevity
    };

    var mockContext = new Mock<IMyContext>();
    mockContext.Setup(m => m.MySettings(username)).Returns(model);

    var controller = new MyController(mockContext.Object) {
        ControllerContext = new ControllerContext {
            HttpContext = mockHttpContext.Object
        }
    };

    //Act
    var viewResult = controller.Index() as ViewResult;

    //Assert
    Assert.IsNotNull(viewResult);
    Assert.IsNotNull(viewResult.Model);
    Assert.AreEqual(model, viewResult.Model);
}

3

বিদ্যমান ক্লাসগুলি ব্যবহার করার সম্ভাবনাও রয়েছে এবং প্রয়োজন হলে কেবল উপহাস করা যায়।

var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
    HttpContext = new DefaultHttpContext
    {
        User = user.Object
    }
};

3

আমার ক্ষেত্রে, আমি ব্যবহার করতে প্রয়োজন Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Nameএবং কিছু ব্যবসা লজিক নিয়ন্ত্রকের বাইরে বসে। আমি এর জন্য এনকোসির, ক্যালিনস এবং পোকের উত্তরগুলির সংমিশ্রণটি ব্যবহার করতে সক্ষম হয়েছি:

var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");

var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);

var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();

var controller = new MyController(...);

var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);

controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
    User = mockPrincipal.Object
};

var result = controller.Get() as OkObjectResult;
//Assert results

mockAuthHandler.Verify();

2

আমি একটি বিমূর্ত কারখানা প্যাটার্ন বাস্তবায়ন করতে চাই।

বিশেষ করে ব্যবহারকারীর নাম দেওয়ার জন্য একটি কারখানার জন্য একটি ইন্টারফেস তৈরি করুন।

তারপরে কংক্রিট ক্লাস সরবরাহ করুন, একটি সরবরাহ করে User.Identity.Nameএবং এমন একটি যা আপনার পরীক্ষার জন্য কাজ করে এমন কিছু হার্ড কোডড মান সরবরাহ করে।

তারপরে আপনি উত্পাদন বনাম পরীক্ষার কোডের উপর নির্ভর করে উপযুক্ত কংক্রিট শ্রেণি ব্যবহার করতে পারেন। সম্ভবত কারখানাটি প্যারামিটার হিসাবে পাস করার জন্য খুঁজছেন বা কিছু কনফিগারেশন মানের উপর ভিত্তি করে সঠিক কারখানায় স্যুইচ করছেন।

interface IUserNameFactory
{
    string BuildUserName();
}

class ProductionFactory : IUserNameFactory
{
    public BuildUserName() { return User.Identity.Name; }
}

class MockFactory : IUserNameFactory
{
    public BuildUserName() { return "James"; }
}

IUserNameFactory factory;

if(inProductionMode)
{
    factory = new ProductionFactory();
}
else
{
    factory = new MockFactory();
}

SettingsViewModel svm = _context.MySettings(factory.BuildUserName());

ধন্যবাদ. আমি আমার জিনিসগুলির জন্য অনুরূপ কিছু করছি । আমি কেবল আশা করছিলাম যে আইপ্রিনিকপাল এর মতো সাধারণ জিনিসগুলির জন্য "বাক্সের বাইরে" কিছু থাকবে। তবে দৃশ্যত, না!
ফেলিক্স

অতিরিক্তভাবে, ব্যবহারকারী কন্ট্রোলারবেসের সদস্য পরিবর্তনশীল। এ কারণেই এএসপি.নেট এর পূর্ববর্তী সংস্করণগুলিতে লোকেরা এইচটিটিপি কনটেক্সটকে উপহাস করেছিল এবং সেখান থেকে আইপিআরসিস্ট্যানাল পাচ্ছিল। প্রোডাকশন
ফেলিক্স

1

আমি আমার কন্ট্রোলারগুলিকে সরাসরি আঘাত করতে চাই এবং কেবল অটোফ্যাকের মতো ডিআই ব্যবহার করতে চাই। এটি করার জন্য আমি প্রথমে নিবন্ধন করছি ContextController

var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
    User = new GenericPrincipal(identity, null)
};

var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);

পরবর্তী আমি যখন নিয়ন্ত্রকদের নিবন্ধভুক্ত করি তখন আমি সম্পত্তি ইনজেকশন সক্ষম করি।

  builder.RegisterAssemblyTypes(assembly)
                    .Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();

তারপরে User.Identity.Nameজনবহুল হয় এবং আমার কন্ট্রোলারে কোনও পদ্ধতি কল করার সময় আমার বিশেষ কিছু করার দরকার নেই।

public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
    var requestedBy = User.Identity?.Name;
    ..................
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.