स्प्रिंग एमवीसी परीक्षण के साथ "परिपत्र दृश्य पथ" अपवाद से कैसे बचें


117

मेरे नियंत्रकों में मेरा निम्नलिखित कोड है:

@Controller
@RequestMapping("/preference")
public class PreferenceController {

    @RequestMapping(method = RequestMethod.GET, produces = "text/html")
    public String preference() {
        return "preference";
    }
}

मैं बस स्प्रिंग एमवीसी टेस्ट का उपयोग करके इसका परीक्षण करने की कोशिश कर रहा हूं :

@ContextConfiguration
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {

    @Autowired
    private WebApplicationContext ctx;

    private MockMvc mockMvc;
    @Before
    public void setup() {
        mockMvc = webAppContextSetup(ctx).build();
    }

    @Test
    public void circularViewPathIssue() throws Exception {
        mockMvc.perform(get("/preference"))
               .andDo(print());
    }
}

मुझे निम्नलिखित अपवाद मिल रहे हैं:

परिपत्र दृश्य पथ [वरीयता]: वर्तमान हैंडलर URL [/ वरीयता] को फिर से वापस भेजेगा। अपने ViewResolver सेटअप की जाँच करें! (संकेत: यह डिफ़ॉल्ट दृश्य नाम की पीढ़ी के कारण अनिर्दिष्ट दृश्य का परिणाम हो सकता है।)

जब मुझे अजीब लगता है कि यह ठीक काम करता है जब मैं "पूर्ण" संदर्भ कॉन्फ़िगरेशन को लोड करता हूं जिसमें टेम्पलेट और व्यू रिज़ॉल्वर शामिल हैं जैसा कि नीचे दिखाया गया है:

<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="webTemplateResolver">
    <property name="prefix" value="WEB-INF/web-templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="characterEncoding" value="UTF-8" />
    <property name="order" value="2" />
    <property name="cacheable" value="false" />
</bean>

मुझे अच्छी तरह पता है कि टेम्पलेट रिज़ॉल्वर द्वारा जोड़ा गया उपसर्ग यह सुनिश्चित करता है कि जब ऐप इस टेम्पलेट रिज़ॉल्वर का उपयोग करता है तो "परिपत्र दृश्य पथ" नहीं है।

लेकिन फिर मुझे स्प्रिंग एमवीसी टेस्ट का उपयोग करके अपने ऐप का परीक्षण कैसे करना चाहिए?


1
क्या आप ViewResolverउपयोग कर सकते हैं पोस्ट जब यह विफल हो रहा है?
सोथिरियोस डेलिमोनोलिस 15'13

@ सोतिरोसिलिमैनोलिस: मुझे यकीन नहीं है कि स्प्रिंग एमवीसी टेस्ट द्वारा किसी भी दृश्य का उपयोग किया जाता है। प्रलेखन
बाल्टो

8
मैं एक ही समस्या का सामना कर रहा था, लेकिन समस्या यह थी कि मैं निर्भरता से नीचे नहीं जोड़ा गया था। <निर्भरता> <groupId> org.springframework.boot </ groupId> <विरूपण साक्ष्य> स्प्रिंग-बूट-स्टार्टर-थाइमेल्फ </ विरूपण साक्ष्य> </ निर्भरता>
आमिर

@RestControllerइसके बजाय का उपयोग करें@Controller
MozenRath

जवाबों:


65

इसका स्प्रिंग एमवीसी परीक्षण से कोई लेना-देना नहीं है।

जब आप एक की घोषणा नहीं करते हैं ViewResolver, तो स्प्रिंग एक डिफ़ॉल्ट को पंजीकृत करता है InternalResourceViewResolverजो JstlViewप्रतिपादन के लिए उदाहरण प्रस्तुत करता है View

JstlViewवर्ग फैली InternalResourceViewहै जो

एक ही वेब अनुप्रयोग के भीतर एक JSP या अन्य संसाधन के लिए आवरण। अनुरोध के रूप में मॉडल ऑब्जेक्ट का विस्तार करता है और javax.servlet.RequestDisperer का उपयोग करके निर्दिष्ट संसाधन URL के लिए अनुरोध करता है।

इस दृश्य के लिए एक URL को वेब अनुप्रयोग के भीतर एक संसाधन निर्दिष्ट करना चाहिए, जो कि RequestDispatcher के आगे या विधि को शामिल करने के लिए उपयुक्त है।

बोल्ड मेरा है। दूसरे पासवर्ड में, रेंडर करने से पहले का दृश्य, RequestDispatcherकिस को किस करने की कोशिश करेगा forward()। ऐसा करने से पहले यह निम्नलिखित की जाँच करता है

if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {
    throw new ServletException("Circular view path [" + path + "]: would dispatch back " +
                        "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +
                        "(Hint: This may be the result of an unspecified view, due to default view name generation.)");
}

pathदृश्य नाम कहां है, आप किस से लौटे हैं @Controller। इस उदाहरण में, वह है preference। वैरिएबल uriहैंडल का आग्रह रखता है, जो है /context/preference

ऊपर दिए गए कोड से पता चलता है कि यदि आप आगे की ओर थे /context/preference, तो उसी सर्वलेट (पिछले हिस्से को उसी से हैंडल करने के बाद) अनुरोध को हैंडल करेगा और आप एक अंतहीन लूप में जाएंगे।


जब आप किसी की घोषणा ThymeleafViewResolverऔर एक ServletContextTemplateResolverएक विशिष्ट साथ prefixऔर suffix, यह बनाता है Viewअलग ढंग से, ऐसा लगता है एक रास्ता दे रही है

WEB-INF/web-templates/preference.html

ThymeleafViewउदाहरण फ़ाइल ServletContextका उपयोग करके पथ के सापेक्ष फ़ाइल का पता लगाते हैंServletContextResourceResolver

templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`

जो अंततः

return servletContext.getResourceAsStream(resourceName);

यह एक संसाधन प्राप्त करता है जो ServletContextपथ के सापेक्ष है । यह TemplateEngineHTML उत्पन्न करने के लिए उपयोग कर सकता है । कोई रास्ता नहीं है एक अंतहीन पाश यहाँ हो सकता है।


1
आपके विस्तृत उत्तर के लिए धन्यवाद। मुझे समझ में नहीं आता कि जब मैं थाइमेल्फ का उपयोग करता हूं तो लूप क्यों नहीं होता है और जब मैं थाइमेल्फ व्यू रिज़ॉल्वर का उपयोग नहीं करता हूं तो यह क्यों होता है। हालाँकि, मुझे अभी भी यकीन नहीं है कि मेरा
कॉन्फिगरेशन

1
@balteo जब आप उपयोग ThymleafViewResolverकरते हैं तो आपको और आपके द्वारा प्रदान की गई Viewफ़ाइल के रूप में हल किया जाता है । जब आप उस रिज़ॉल्यूशन का उपयोग नहीं करते हैं, तो स्प्रिंग एक डिफ़ॉल्ट का उपयोग करता है जो संसाधनों को एक के साथ पाता है । यह संसाधन एक हो सकता है । इस मामले में ऐसा इसलिए है क्योंकि पथ आपके लिए मैप करता है । prefixsuffixInternalResourceViewResolverRequestDispatcherServlet/preferenceDispatcherServlet
सोटिरिओस डेलिमोनोलिस 15'13

2
@balteo अपने ऐप का परीक्षण करने के लिए, एक सही प्रदान करें ViewResolver। या तो ThymeleafViewResolverआपके प्रश्न के अनुसार, आपके स्वयं के कॉन्फ़िगर InternalResourceViewResolverया आपके नियंत्रक में वापस आ रहे दृश्य नाम को बदल दें।
सोतिरियोस डेलिमोलिस

धन्यवाद धन्यवाद धन्यवाद! मैं यह पता नहीं लगा सका कि आंतरिक संसाधन दृश्य रिज़ॉल्वर को "शामिल" के बजाय आगे करने के लिए क्यों पसंद किया गया था, लेकिन अब आपकी व्याख्या के साथ ऐसा लगता है जैसे नाम में "संसाधन" का उपयोग थोड़ा अस्पष्ट है। यह व्याख्या तारकीय है।
क्रिस थॉम्पसन

2
@ShirgillFarhanAnsari एक @RequestMappingएनोटेट हैंडलर पद्धति है जिसमें Stringरिटर्न टाइप (और नहीं @ResponseBody) है, जिसका रिटर्न वैल्यू एक हैंडल है, ViewNameMethodReturnValueHandlerजो स्ट्रिंग को एक दृश्य नाम के रूप में व्याख्या करता है, और इसका उपयोग मैं उस प्रक्रिया से गुजरने के लिए करता हूं जो मैं अपने उत्तर में समझाता हूं। इसके साथ @ResponseBody, स्प्रिंग MVC इसके RequestResponseBodyMethodProcessorबजाय सीधे HTTP प्रतिक्रिया के लिए स्ट्रिंग लिखता है, जिसका उपयोग करेगा । कोई दृश्य संकल्प नहीं।
सोतीरियोस डेलिमोलिसिस

97

मैंने नीचे इस तरह @ResponseBody का उपयोग करके इस समस्या को हल किया:

@RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @Transactional(value = "jpaTransactionManager")
    public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) {

10
वे एक दृश्य को हल करके HTML को वापस करना चाहते हैं, एक के क्रमबद्ध संस्करण को वापस नहीं करते हैं List<DomainObject>
सोतीरियोस डेलिमोलिस

2
स्प्रिंग आराम वेब सेवा के लिए JSON प्रतिक्रिया देते समय इसने मेरे मुद्दे को हल किया ..
जो

अच्छा है, अगर मैं निर्दिष्ट नहीं करता है = {"आवेदन / json"}, फिर भी यह काम करता है। क्या यह डिफ़ॉल्ट रूप से json उत्पन्न करता है?
Jay

74

@Controller@RestController

मेरे पास एक ही मुद्दा था और मैंने देखा कि मेरे नियंत्रक के साथ भी एनोटेट किया गया था @Controller। इसे @RestControllerहल करने से समस्या हल हो गई। यहाँ स्प्रिंग वेब MVC से स्पष्टीकरण दिया गया है :

@RestController एक बना हुआ एनोटेशन है जो स्वयं meta- एनोट्रोलर @Controller और @ResponseBody के साथ होता है जो एक नियंत्रक को दर्शाता है जिसका हर तरीका टाइप-लेवल @ResponseBody एनोटेशन विरासत में मिलता है और इसलिए प्रतिक्रिया बॉडी बनाम व्यू रेजोल्यूशन और HTML टेम्पलेट के साथ रेंडर करने के लिए सीधे लिखता है।


1
@TodorTodorov यह मेरे लिए किया था
इगोर रोड्रिगेज

@TodorTodorov और मेरे लिए!
रैन

3
मेरे लिए भी काम किया। मेरे पास @ControllerAdviceइसमें एक handleXyExceptionविधि थी, जिसने एक ResponseEntity के बजाय मेरी अपनी वस्तु वापस कर दी। एनोटेशन के @RestControllerशीर्ष पर जोड़कर @ControllerAdviceकाम किया और समस्या दूर हो गई है।
इगोर

36

मैंने इस समस्या को हल किया है:

@Before
    public void setup() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/view/");
        viewResolver.setSuffix(".jsp");

        mockMvc = MockMvcBuilders.standaloneSetup(new HelpController())
                                 .setViewResolvers(viewResolver)
                                 .build();
    }

1
यह केवल वृषण के लिए है। नियंत्रकों के लिए नहीं।
cst1992

2
अपने नए यूनिट परीक्षणों में से किसी में इस समस्या का निवारण करने में किसी की मदद कर रहा था, यह वही है जिसकी हम तलाश कर रहे थे।
ब्रैडफोर्ड

मैंने इसका उपयोग किया, लेकिन परीक्षण में मेरी रिज़ॉल्वर के लिए गलत उपसर्ग और प्रत्यय देने के बावजूद, इसने काम किया। क्या आप इसके पीछे तर्क दे सकते हैं, इसकी आवश्यकता क्यों है?
दुष्यंतषु

यह उत्तर सबसे सही और विशिष्ट होने के लिए मतदान किया जाना चाहिए
कैफीन कोडर

20

मैं एक वेबपेज को आजमाने और लोड करने के लिए स्प्रिंग बूट का उपयोग कर रहा हूं, परीक्षण नहीं, और यह समस्या थी। मेरा समाधान थोड़ा अलग परिस्थितियों पर विचार करने से ऊपर से थोड़ा अलग था। (हालांकि उन जवाबों ने मुझे समझा दिया है।)

मुझे बस मैवेन में अपने स्प्रिंग बूट स्टार्टर निर्भरता को बदलना था:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

सेवा:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

बस 'वेब' को 'थाइमेलफ' में बदलने से मेरे लिए समस्या तय हो गई।


1
मेरे लिए, स्टार्टर-वेब को बदलना आवश्यक नहीं था, लेकिन मेरे पास <स्कोलिम> टेस्ट </ स्कोप> के साथ थाइमेलफ पर निर्भरता थी। जब मैंने "परीक्षण" गुंजाइश को हटा दिया, तो यह काम किया। सुराग के लिए धन्यवाद!
जॉर्जिना डियाज़

16

यदि आप वास्तव में दृश्य प्रस्तुत करने के बारे में परवाह नहीं करते हैं तो यहां एक आसान समाधान है।

InternalResourceViewResolver का एक उपवर्ग बनाएँ, जो वृत्ताकार दृश्य पथों की जाँच नहीं करता है:

public class StandaloneMvcTestViewResolver extends InternalResourceViewResolver {

    public StandaloneMvcTestViewResolver() {
        super();
    }

    @Override
    protected AbstractUrlBasedView buildView(final String viewName) throws Exception {
        final InternalResourceView view = (InternalResourceView) super.buildView(viewName);
        // prevent checking for circular view paths
        view.setPreventDispatchLoop(false);
        return view;
    }
}

फिर इसके साथ अपना परीक्षण सेट करें:

MockMvc mockMvc;

@Before
public void setUp() {
    final MyController controller = new MyController();

    mockMvc =
            MockMvcBuilders.standaloneSetup(controller)
                    .setViewResolvers(new StandaloneMvcTestViewResolver())
                    .build();
}

इससे मेरी समस्या ठीक हो गई। मैंने परीक्षण की एक ही निर्देशिका में एक स्टैंडअलोनमिक्सटैस्ट व्यूएंडोल्वर वर्ग जोड़ा और इसका उपयोग ऊपर बताए अनुसार मोकमबैक्बुइल्डर्स में किया। धन्यवाद
माथिउस अराउजो

मुझे भी यही समस्या थी और इसने मेरे लिए भी इसे ठीक कर दिया। आपका बहुत बहुत धन्यवाद!
जोहान

यह एक महान समाधान है कि (1) को नियंत्रकों को बदलने की आवश्यकता नहीं है और (2) सभी परीक्षण वर्गों में एक साधारण आयात के साथ पुन: उपयोग किया जा सकता है। +1
नंदेर स्पेरस्ट्रा

Oldie लेकिन गोल्डी! मेरा दिन बचाया। इस समाधान का +1 के लिए धन्यवाद
Raistlin

13

यदि आप स्प्रिंग बूट का उपयोग कर रहे हैं, तो अपने pom.xml में thymeleaf निर्भरता जोड़ें:

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>2.1.6.RELEASE</version>
    </dependency>

1
वोट दें। गुम Thymeleaf निर्भरता क्या मेरी परियोजना में इस त्रुटि का कारण था। हालाँकि, यदि आप स्प्रिंग बूट का उपयोग कर रहे हैं, तो निर्भरता इस तरह <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
दिखेगी


8

मेरे मामले में, मैं कोटलिन + स्प्रिंग बूट की कोशिश कर रहा था और मैं परिपत्र दृश्य पथ मुद्दे में मिला। मेरे द्वारा ऑनलाइन प्राप्त सभी सुझाव मदद नहीं कर सकते, जब तक कि मैंने नीचे की कोशिश नहीं की:

मूल रूप से मैंने अपने नियंत्रक का उपयोग करके एनोटेट किया था @Controller

import org.springframework.stereotype.Controller

मैंने तब @Controllerसाथ दिया@RestController

import org.springframework.web.bind.annotation.RestController

और इसने काम किया।


6

यदि आपने @RequestBody का उपयोग नहीं किया है और इसका उपयोग करने का केवल @Controllerसबसे सरल तरीका है, @RestControllerइसके बजाय इसका उपयोग करना है@Controller


यह ठीक नहीं है, अब यह टेम्पलेट दिखाने के बजाय, आपकी फ़ाइल का नाम दिखाएगा
आशीष कांबले

1
यह वास्तविक समस्या पर निर्भर करता है। यह त्रुटि कई कारणों से हो सकती है
MozenRath

4

एनोटेशन @ResponseBodyको अपनी विधि वापसी में जोड़ें ।


कृपया यह बताएं कि यह समस्या कैसे और क्यों हल करती है, इससे वास्तव में आपके पोस्ट की गुणवत्ता को बेहतर बनाने में मदद मिलेगी, और संभवत: अधिक वोट मिले।
एंड्रॉइड

3

मैं Thymeleaf के साथ स्प्रिंग बूट का उपयोग कर रहा हूं। इसी से मेरा काम बना है। जेएसपी के साथ भी ऐसे ही उत्तर हैं लेकिन ध्यान दें कि मैं HTML का उपयोग कर रहा हूं, जेएसपी का नहीं, और ये फ़ोल्डर में हैं src/main/resources/templatesजैसे कि एक मानक स्प्रिंग बूट प्रोजेक्ट में जैसा कि यहां बताया गया है । यह आपका मामला भी हो सकता है।

@InjectMocks
private MyController myController;

@Before
public void setup()
{
    MockitoAnnotations.initMocks(this);

    this.mockMvc = MockMvcBuilders.standaloneSetup(myController)
                    .setViewResolvers(viewResolver())
                    .build();
}

private ViewResolver viewResolver()
{
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

    viewResolver.setPrefix("classpath:templates/");
    viewResolver.setSuffix(".html");

    return viewResolver;
}

उम्मीद है की यह मदद करेगा।


3

यदि पृष्ठ दिखाई देने पर स्प्रिंग बूट + फ़्रीमार्कर चला रहा हो:

व्हिटेलबेल त्रुटि पृष्ठ इस एप्लिकेशन के पास / त्रुटि के लिए कोई स्पष्ट मानचित्रण नहीं है, इसलिए आप इसे एक गिरावट के रूप में देख रहे हैं।

वसंत-बूट-स्टार्टर-माता-पिता में 2.2.1.रिलीज़ संस्करण फ़्रीमार्कर काम नहीं करता है:

  1. .mxt से
  2. Application.properties में जोड़ें: spring.fremarker.expose-request-विशेषताएँ = सच

spring.fremarker.suffix = .ftl


1
बस .ftl से .ftlh में। नाम बदलने से मेरे लिए समस्या हल हो गई है।
जननिक

यार ... मैं तुम्हें एक बियर देना। मैंने अपना पूरा दिन इसी नाम बदलने की वजह से खोया।
julianobrasil

2

थाइमेलफ के लिए:

मैंने बस वसंत 4 और थाइमेलफ का उपयोग करना शुरू किया, जब मुझे इस त्रुटि का सामना करना पड़ा, तो इसे जोड़कर हल किया गया था:

<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
  <property name="templateEngine" ref="templateEngine" />
  <property name="order" value="0" />
</bean> 

1

का उपयोग करते समय @Controllerएनोटेशन, आप की जरूरत @RequestMappingहै और @ResponseBodyएनोटेशन। एनोटेशन जोड़ने के बाद पुन: प्रयास करें@ResponseBody


0

मैं स्प्रिंग वेब ऐप को कॉन्फ़िगर करने के लिए एनोटेशन का उपयोग करता हूं InternalResourceViewResolver, कॉन्फ़िगरेशन में बीन जोड़कर हल की गई समस्या । आशा है कि यह मददगार होगा।

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example.springmvc" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
}

धन्यवाद यह मेरे लिए ठीक काम करता है। मेरा ऐप 1.2.7 से स्प्रिंग बूट 1.3.1 में अपग्रेड करने के बाद टूट गया और केवल यही लाइन थी जो कि रजिस्ट्री को विफल कर रही थी ।addViewController ("/ लॉगिन")। setViewName ("लॉगिन"); उस बीन को पंजीकृत करते समय, ऐप ने फिर से काम किया ... कम से कम लॉगिन wll गया।
le0diaz

0

यह इसलिए हो रहा है क्योंकि वसंत "वरीयता" को हटा रहा है और "वरीयता" को जोड़कर फिर से उरी के समान मार्ग बना रहा है।

इस तरह से हो रहा है: अनुरोध उरी: "/ वरीयता"

"वरीयता" निकालें: "/"

पथ पथ: "/" + "वरीयता"

अंत स्ट्रिंग: "/ वरीयता"

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

अपने हित में इसका सबसे अच्छा एक अलग दृश्य नाम देना पसंद करते हैं जैसे "वरीयता दृश्य" या आपकी पसंद की कोई भी चीज़।


0

संकलित करने का प्रयास करें ("org.springframework.boot: spring-boot-starter-thymeleaf") आपके ग्रेडल फ़ाइल पर निर्भरता। इसमैलिफ़ विचारों को मैप करने में मदद करता है।


0

मेरे मामले में, स्प्रिंग बूट एप्लिकेशन का उपयोग करके जेएसपी पृष्ठों की सेवा करने की कोशिश करते समय मुझे यह समस्या थी।

यहाँ मेरे लिए क्या काम किया गया है:

application.properties

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

pom.xml

JSP के लिए समर्थन सक्षम करने के लिए, हमें tomcat-embed-jasper पर निर्भरता जोड़ने की आवश्यकता होगी।

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>

-2

एक और सरल दृष्टिकोण:

package org.yourpackagename;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

      @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(PreferenceController.class);
        }


    public static void main(String[] args) {
        SpringApplication.run(PreferenceController.class, args);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.